优先级队列 - 简书一、定义 优先级队列有很多种实现方式。其中使用 “堆” 来实现 “优先队列” 是最常见的,堆的底层是完全二叉树的形式。 上述是一个小顶堆(最小堆)的示意图 最小堆是一种经过排序的完全...
一、定义
优先级队列有很多种实现方式。其中使用 “堆” 来实现 “优先队列” 是最常见的,堆的底层是完全二叉树的形式。
1-0 堆的示意图
上述是一个小顶堆(最小堆)的示意图
最小堆是一种经过排序的完全二叉树,其中任一非终端节点的数据值均不大于其左子节点和右子节点的值。
二、API
2-0 大顶堆的 API 定义
2.1 上浮和下沉
堆的操作中,最重要的就是堆元素的上浮和下沉操作:
-
上浮(siftup)
在堆中插入元素后(完全二叉树的最右下方插入),需要进行上浮操作,重新使得堆有序。2-1-1 大顶堆上浮元素
private void swim(int k) { while (k > 1 && (a[k]>a[k/2])) { exch(k, k/2); k = k/2; } }
-
下沉(siftdown)
当删除一个堆元素(堆顶)时,首先将堆顶元素与最右下方元素交换,然后删除。此时堆顶元素需要进行下沉操作,重新使得堆有序。2-1-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; swap(k, j); k = j; } }
2.2 插入元素
新增元素添加到树的底层最右侧,然后上浮。
2-2-1 大顶堆的插入
2.3 删除最大元素
将树的最后一个元素与第一个元素交换,删除最后一个元素,然后从堆顶开始下沉。
2-3-1 大顶堆删除最大元素
三、完整实现
3.1 大顶堆
3-1-1 大顶堆的操作用例
public class MaxPQ<Key> implements Iterable<Key> { private Key[] pq; private int n; private Comparator<Key> 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; } private boolean isMaxHeap() { return isMaxHeap(1); } private boolean isMaxHeap(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 isMaxHeap(left) && isMaxHeap(right); } public Iterator<Key> iterator() { return new HeapIterator(); } private class HeapIterator implements Iterator<Key> { private MaxPQ<Key> copy; 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(); } } }
3.2 小顶堆
public class MinPQ<Key> implements Iterable<Key> { private Key[] pq; private int n; private Comparator<Key> 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) { if (n == pq.length - 1) resize(2 * pq.length); 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; 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() { return isMinHeap(1); } private boolean isMinHeap(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 isMinHeap(left) && isMinHeap(right); } public Iterator<Key> iterator() { return new HeapIterator(); } private class HeapIterator implements Iterator<Key> { private MinPQ<Key> copy; 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(); } } }
四、性能分析
- 时间复杂度
插入操作:O(lgN)
删除操作:O(lgN)
建堆:O(NlgN) - 空间复杂度
O(N)
来源:CSDN
作者:player丶
链接:https://blog.csdn.net/qq_15766181/article/details/103538873