优先队列的多种实现方式与比较
优先队列
很多情况下,我们需要对一组数据进行处理,处理不同特征的元素
就像电脑处理不同的程序一样,需要为每个元素进行优先级划分,总是处理优先级高的元素或是程序
而这样的数据结构应该支持:
- 删除最大元素(优先级最高先执行)
- 插入元素(优先级高的可以进行“插队”)
一些应用场景:
- 模拟系统:按照时间顺序处理事件
- 任务调度:键值的优先级决定了应该首先执行哪些任务
- 数值计算
优先队列可以通过简单的数组,链表,堆来实现
Sort ├─ MaxPQ.java // 通过堆实现优先队列 ├─ OrderedArrayTopM.java // 通过有序数组实现 ├─ TopM.java // 优先队列实例代码 ├─ UnorderedArrayTopM.java // 通过无序数组实现
UnorderedArrayTopM.java
package Sort; import static Sort.SortExample.exch; import static Sort.SortExample.less; // 通过无序数组实现优先队列 public class UnorderedArrayTopM<Key extends Comparable<Key>> { Key[] pq; // elements int n; // number of elements // set inititial size of heap to hold size elements // 设置堆的初始大小以容纳大小元素 public UnorderedArrayTopM(int capacity) { pq = (Key[]) new Comparable[capacity]; n = 0; } public boolean isEmpty() { return n == 0; } public int size() { return n; } public void insert(Key x) { pq[n++] = x; } public Key delMax() { int max = 0; for (int i = 1; i < n; i++) // for循环找到最大元素 if (less(max, i)) max = i; exch(pq, max, n-1); return pq[--n]; } }
OrderedArrayTopM.java
package Sort; import static Sort.SortExample.less; // 通过有序数组实现优先队列 public class OrderedArrayTopM<Key extends Comparable<Key>> { private Key[] pq; // elements private int n; // number of elements // set inititial size of heap to hold size elements public OrderedArrayTopM(int capacity) { pq = (Key[]) (new Comparable[capacity]); n = 0; } public boolean isEmpty() { return n == 0; } public int size() { return n; } public Key delMax() { return pq[--n]; } public void insert(Key key) { int i = n-1; // 从后往前遍历 直到有元素不小于插入元素key while (i >= 0 && less(key, pq[i])) { pq[i+1] = pq[i]; // 向后移 i--; } pq[i+1] = key; // 插入 n++; // 元素个数加一 } }
MaxPQ.java
package Sort; import java.util.Comparator; import java.util.Iterator; import java.util.NoSuchElementException; import static Sort.SortExample.less; // 通过堆实现优先队列 public class MaxPQ<Key> implements Iterable<Key> { private Key[] pq; // 将元素存储在 1 到 n 之间 0处不传值 private int n; // 节点的数量 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); } // 将传入数组复制到 pq数组中 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) { // 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 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 void exch(int i, int j) { Key swap = pq[i]; pq[i] = pq[j]; pq[j] = swap; } // 判断是否是满堆 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); } // 是否是一个最大堆 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> { // create a new pq 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(); } } }
效率测试
数据来源:algo4 官方提供的 algo4-data.zip 数据包
无序数组实现的优先队列, 添加32000个数据,执行时间为:9.892E-4 s 无序数组实现的优先队列, 删除最大元素,执行时间为:0.0076753 s 有序数组实现的优先队列, 添加32000个数据,执行时间为:2.0337085 s 有序数组实现的优先队列, 删除最大元素,执行时间为:3.0E-7 s 基于堆实现的优先队列, 添加32000个数据,执行时间为:0.0067983 s 基于堆实现的优先队列, 删除最大元素,执行时间为:2.63E-5 s
本文作者:清澈的澈
本文链接:https://www.cnblogs.com/lmc7/articles/17531389.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步