CS61B笔记 | Heaps and Priority Queues
- 数据结构决定了如何组织、存储数据,而算法则定义了如何高效地操作这些数据。
- 不同的数据结构适合不同类型的算法,反之,特定的算法往往需要特定的数据结构来实现其效率。
- 数据结构是算法的基础,而算法则是对数据结构的操作过程。
问题引申
我需要这样一个抽象数据结构:Priority Queues(优先级队列),它将具有以下基本功能:
- 添加
- 获取最小元素
- 删除最小元素
- 获取元素个数
/** (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();
}
现在假如有10w个物理粒子,我们需要找出其中10个最大的,如何在最少的内存和时间开销下去找到它们呢?
- 我们可以每次只保存10个粒子,当出现第11个粒子的时候,就removeSmallest(),使得队列中粒子数上限一直为10。循环执行即可找到10w个物理粒子中10个最大的粒子。
现在,考虑如何去实现...
一些糟糕的实现
| Ordered Array | Bushy BST | Hash Table | Heap | |
|---|---|---|---|---|
| add | Θ(N) | Θ(log N) | Θ(1) | |
| getSmallest | Θ(1) | Θ(log N) | Θ(N) | |
| removeSmallest | Θ(N) | Θ(log N) | Θ(N) | |
| Caveats | Dups tough |
- 有序数组,添加和删除都需要Θ(N)的时间复杂度,开销太大,不考虑。
- 二分搜索树,添加删除查找元素都只需要Θ(log N)时间,但是注意二分搜索树很难有重复项,一开始我们就没有考虑往里面添加重复元素。如果使用BST来实现我们的需求,可能需要大量额外操作,也不考虑。
- 哈希表,添加和查找元素需要常数时间,看起来不错。但是在哈希表中如何找到最小元素呢?哈希表中的东西没有真正的顺序,如果要找最小元素需要遍历每一个桶,这并不好玩,所以还是不考虑。
Heap
我想用一个快速而优雅地处理重复项的实现来填充Heap这一列
- 使用的新数据结构叫做 二叉最小堆,它仍然是一个二叉树,但是基本属性有所不同。
- 根节点是最小值,每个节点的值小于或等于子节点的值,所有元素尽量往左边靠拢。
- 如果要添加元素,先添加到合适的叶子节点,保证整棵树的茂盛,然后向上swimming,遇到大于自己的就交换,直到找到合适的位置。
- 如果删除根节点,将根节点替换为堆中最后一个项目,也就是尽可能靠右的底层。然后再向下寻找是否有子节点比自己还小,交换,直到找到合适的位置。
具体代码可以用数组实现,类似于不相交集。也可以类似于之前作业里写过的BST。
| Ordered Array | Bushy BST | Hash Table | Heap | |
|---|---|---|---|---|
| add | Θ(N) | Θ(log N) | Θ(1) | Θ(log N) |
| getSmallest | Θ(1) | Θ(log N) | Θ(N) | Θ(1) |
| removeSmallest | Θ(N) | Θ(log N) | Θ(N) | Θ(log N) |
| Caveats | Dups tough |
堆对重复项非常擅长!
目前学习的所有数据结构,都有一些共同点:
- 用户可以添加新项目到项目堆中。(数据结构要做的就是保持项目堆的有序性)
- 应该有某个操作可以让用户从项目堆中取出项目。(通过有序性,这样做可以是高效的)
所以我们花了很长时间,以不同并且创造性的方式来解决的都是搜索问题,非常酷!
以上几种数据结构是CS61B的核心部分,之后开始学习图。

浙公网安备 33010602011771号