课程概要
- 优先级队列
- 堆
- 总结
优先级队列(Priority Queue)
接口设计
/** (Min) Priority Queue: Allowing tracking and removal of the
* smallest item in a priority queue. */
public interface MinPQ<Item> {
/** Adds the item to the priority queue. */
public void add(Item x);
/** Returns the smallest item in the priority queue. */
public Item getSmallest();
/** Removes the smallest item from the priority queue. */
public Item removeSmallest();
/** Returns the size of the priority queue. */
public int size();
}
使用场景示例
- 网络监听市民的聊天记录,找出最不和谐的 M 条对话 (Top K 问题)
- 一般实现:创建包含所有对话的列表,并用 HarmoniousnessComparator 比较器来筛选出最不和谐的 M 条记录
public List<String> unharmoniousTexts(Sniffer sniffer, int M) {
ArrayList<String> allMessages = new ArrayList<String>();
for (Timer timer = new Timer(); timer.hours() < 24; ) {
allMessages.add(sniffer.getNextMessage());
}
Comparator<String> cmptr = new HarmoniousnessComparator();
Collections.sort(allMessages, cmptr, Collections.reverseOrder());
return allMessages.sublist(0, M);
}
上述方案内存占用 O(N),使用 MinPQ 内存占用 O(M), API 也更易使用
public List<String> unharmoniousTexts(Sniffer sniffer, int M) {
Comparator<String> cmptr = new HarmoniousnessComparator();
MinPQ<String> unharmoniousTexts = new HeapMinPQ<Transaction>(cmptr);
for (Timer timer = new Timer(); timer.hours() < 24; ) {
unharmoniousTexts.add(sniffer.getNextMessage());
if (unharmoniousTexts.size() > M)
{ unharmoniousTexts.removeSmallest(); }
}
ArrayList<String> textlist = new ArrayList<String>();
while (unharmoniousTexts.size() > 0) {
textlist.add(unharmoniousTexts.removeSmallest());
}
return textlist;
}
如何实现
- 各种方案对比
堆(Heap)
-
上述方案 BST 可以工作,但是叶子节点维护和重复节点处理比较麻烦
-
二叉最小堆定义:遵守完整性和最小堆规则
最小堆:每个节点小于等于它的子节点 完整性:只在最下层缺失元素,所有节点尽可能靠左(左满右不满)
堆的实现
- 底层存储结构是数组,从小到大依次存储在数组中
- 如果一个节点的位置为 k, 则它的父节点的位置为 [k/2], 它的两个子节点的位置分别为 2k 和 2k + 1
- 添加元素:先暂时添加到堆尾,然后通过上浮算法上浮到合适的位置
- 删除最小元素:将堆尾元素移动到根节点,然后通过下沉算法下沉到合适的位置
- 动画演示[科学上网]
应用:
-
堆排序
1. 构造堆 2. 得到堆顶元素,这个就是最大值 3. 交换堆顶元素和数组中的最后一个元素,此时所有元素中的最大元素已经放到合适位置 4. 对堆进行调整 ,重新让出了最后一个元素的剩余元素中的最大值放到堆顶 5. 重复 2-4 这个步骤,知道堆中剩一个元素为止