数据结构之基于堆的优先队列
优先队列的最重要的操作:删除最大元素(或最小)和插入元素。数据结构二叉堆能够很好的实现队列的基本操作。
二叉堆的结点按照层级顺序放入数组,用长度为N+1的私有数组pq来表示一个大小为N的堆(堆元素放在pq[1]至pq[N]之间,为方便计数,未使用pq[0]),跟节点在位置1,它的子结点在位置2和3,以此类推。位置k的节点的父节点位置为k/2,它的两个子节点位置分别为2k和2k+1。
当一颗二叉树的每个节点都大于等于它的两个子节点时,称为大根堆。
当一颗二叉树的每个节点都小于等于它的两个子节点时,称为小根堆。
堆(大根堆)的有序化:
由下至上的堆有序化(上浮):如果堆的有序状态因为某个节点变得比它的父节点更大而被打破,就需要交换它和它的父节点来修复堆,以此类推,直到遇到一个更大的父节点。
由上至下的堆有序化(下沉):如果堆的有序状态因为某个节点变得比它的两个子节点或者其中之一更小而被打破,需要将它和它的两个子节点中的较大者交换来恢复堆,以此类推,直到它的子节点比它更小或者到达堆的底部。
基于堆的优先队列的Java代码实现:
1.大根堆
package sort; import edu.princeton.cs.algs4.StdIn; import edu.princeton.cs.algs4.StdOut; import java.util.Comparator; import java.util.Iterator; import java.util.NoSuchElementException; public class MaxPQ<Key> implements Iterable<Key> { private Key[] pq; // store items at indices 1 to n private int n; // number of items on priority queue private Comparator<Key> comparator; // optional comparator //初始化构造函数 public MaxPQ(int initCapacity) { pq = (Key[]) new Object[initCapacity + 1]; n = 0; } public MaxPQ() { this(1); } public MaxPQ(int initCapacity, Comparator<Key> comparator) { this.comparator = comparator; pq = (Key[]) new Object[initCapacity + 1]; n = 0; } public MaxPQ(Comparator<Key> comparator) { this(1, comparator); } public MaxPQ(Key[] keys) { n = keys.length; pq = (Key[]) new Object[keys.length + 1]; for (int i = 0; i < n; i++) pq[i+1] = keys[i]; for (int k = n/2; k >= 1; k--) sink(k); //下沉 assert isMaxHeap(); } public boolean isEmpty() { return n == 0; } public int size() { return n; } //返回最大的元素 public Key max() { if (isEmpty()) throw new NoSuchElementException("Priority queue underflow"); return pq[1]; } // 数组扩容 private void resize(int capacity) { assert capacity > n; Key[] temp = (Key[]) new Object[capacity]; for (int i = 1; i <= n; i++) { temp[i] = pq[i]; } pq = temp; } //插入元素 public void insert(Key x) { if (n == pq.length - 1) resize(2 * pq.length); pq[++n] = x; swim(n); //上浮 assert isMaxHeap(); } //删除并返回此优先级队列上的最大键。 public Key delMax() { if (isEmpty()) throw new NoSuchElementException("Priority queue underflow"); Key max = pq[1]; exch(1, n--); sink(1); pq[n+1] = null; if ((n > 0) && (n == (pq.length - 1) / 4)) resize(pq.length / 2); assert isMaxHeap(); return max; } //上浮 private void swim(int k) { while (k > 1 && less(k/2, k)) { exch(k, k/2); k = k/2; } } //下沉 private void sink(int k) { while (2*k <= n) { int j = 2*k; if (j < n && less(j, j+1)) j++; if (!less(k, j)) break; exch(k, j); k = j; } } //比较 private boolean less(int i, int j) { if (comparator == null) { return ((Comparable<Key>) pq[i]).compareTo(pq[j]) < 0; } else { return comparator.compare(pq[i], pq[j]) < 0; } } //交换 private void exch(int i, int j) { Key swap = pq[i]; pq[i] = pq[j]; pq[j] = swap; } // is pq[1..n] a max heap? private boolean isMaxHeap() { for (int i = 1; i <= n; i++) { if (pq[i] == null) return false; } for (int i = n+1; i < pq.length; i++) { if (pq[i] != null) return false; } if (pq[0] != null) return false; return isMaxHeapOrdered(1); } // is subtree of pq[1..n] rooted at k a max heap? private boolean isMaxHeapOrdered(int k) { if (k > n) return true; int left = 2*k; int right = 2*k + 1; if (left <= n && less(k, left)) return false; if (right <= n && less(k, right)) return false; return isMaxHeapOrdered(left) && isMaxHeapOrdered(right); } public Iterator<Key> iterator() { return new HeapIterator(); } private class HeapIterator implements Iterator<Key> { private MaxPQ<Key> copy; // add all items to copy of heap // takes linear time since already in heap order so no keys move public HeapIterator() { if (comparator == null) copy = new MaxPQ<Key>(size()); else copy = new MaxPQ<Key>(size(), comparator); for (int i = 1; i <= n; i++) copy.insert(pq[i]); } public boolean hasNext() { return !copy.isEmpty(); } public void remove() { throw new UnsupportedOperationException(); } public Key next() { if (!hasNext()) throw new NoSuchElementException(); return copy.delMax(); } } public static void main(String[] args) { MaxPQ<String> pq = new MaxPQ<String>(); while (!StdIn.isEmpty()) { String item = StdIn.readString(); if (!item.equals("-")) pq.insert(item); else if (!pq.isEmpty()) StdOut.print(pq.delMax() + " "); } StdOut.println("(" + pq.size() + " left on pq)"); } }
2.小根堆
package sort; import edu.princeton.cs.algs4.StdIn; import edu.princeton.cs.algs4.StdOut; import java.util.Comparator; import java.util.Iterator; import java.util.NoSuchElementException; public class MinPQ<Key> implements Iterable<Key> { private Key[] pq; // store items at indices 1 to n private int n; // number of items on priority queue private Comparator<Key> comparator; // optional comparator public MinPQ(int initCapacity) { pq = (Key[]) new Object[initCapacity + 1]; n = 0; } public MinPQ() { this(1); } public MinPQ(int initCapacity, Comparator<Key> comparator) { this.comparator = comparator; pq = (Key[]) new Object[initCapacity + 1]; n = 0; } public MinPQ(Comparator<Key> comparator) { this(1, comparator); } public MinPQ(Key[] keys) { n = keys.length; pq = (Key[]) new Object[keys.length + 1]; for (int i = 0; i < n; i++) pq[i+1] = keys[i]; for (int k = n/2; k >= 1; k--) sink(k); assert isMinHeap(); } //判断是否为空 public boolean isEmpty() { return n == 0; } //大小 public int size() { return n; } //返回最小元素 public Key min() { if (isEmpty()) throw new NoSuchElementException("Priority queue underflow"); return pq[1]; } // 数组扩容 private void resize(int capacity) { assert capacity > n; Key[] temp = (Key[]) new Object[capacity]; for (int i = 1; i <= n; i++) { temp[i] = pq[i]; } pq = temp; } //插入元素 public void insert(Key x) { // double size of array if necessary if (n == pq.length - 1) resize(2 * pq.length); // add x, and percolate it up to maintain heap invariant pq[++n] = x; swim(n); assert isMinHeap(); } //删除最小元素 public Key delMin() { if (isEmpty()) throw new NoSuchElementException("Priority queue underflow"); Key min = pq[1]; exch(1, n--); sink(1); pq[n+1] = null; // to avoid loiterig and help with garbage collection if ((n > 0) && (n == (pq.length - 1) / 4)) resize(pq.length / 2); assert isMinHeap(); return min; } //上浮 private void swim(int k) { while (k > 1 && greater(k/2, k)) { exch(k, k/2); k = k/2; } } //下沉 private void sink(int k) { while (2*k <= n) { int j = 2*k; if (j < n && greater(j, j+1)) j++; if (!greater(k, j)) break; exch(k, j); k = j; } } //比较 private boolean greater(int i, int j) { if (comparator == null) { return ((Comparable<Key>) pq[i]).compareTo(pq[j]) > 0; } else { return comparator.compare(pq[i], pq[j]) > 0; } } //交换元素 private void exch(int i, int j) { Key swap = pq[i]; pq[i] = pq[j]; pq[j] = swap; } private boolean isMinHeap() { for (int i = 1; i <= n; i++) { if (pq[i] == null) return false; } for (int i = n+1; i < pq.length; i++) { if (pq[i] != null) return false; } if (pq[0] != null) return false; return isMinHeapOrdered(1); } private boolean isMinHeapOrdered(int k) { if (k > n) return true; int left = 2*k; int right = 2*k + 1; if (left <= n && greater(k, left)) return false; if (right <= n && greater(k, right)) return false; return isMinHeapOrdered(left) && isMinHeapOrdered(right); } public Iterator<Key> iterator() { return new HeapIterator(); } private class HeapIterator implements Iterator<Key> { private MinPQ<Key> copy; // add all items to copy of heap // takes linear time since already in heap order so no keys move public HeapIterator() { if (comparator == null) copy = new MinPQ<Key>(size()); else copy = new MinPQ<Key>(size(), comparator); for (int i = 1; i <= n; i++) copy.insert(pq[i]); } public boolean hasNext() { return !copy.isEmpty(); } public void remove() { throw new UnsupportedOperationException(); } public Key next() { if (!hasNext()) throw new NoSuchElementException(); return copy.delMin(); } } public static void main(String[] args) { MinPQ<String> pq = new MinPQ<String>(); while (!StdIn.isEmpty()) { String item = StdIn.readString(); if (!item.equals("-")) pq.insert(item); else if (!pq.isEmpty()) StdOut.print(pq.delMin() + " "); } StdOut.println("(" + pq.size() + " left on pq)"); } }