典型的优先级队列需要以下操作才能有效。
- 获取最高优先级元素(获取最小值或最大值)
- 插入一个元素
- 删除最高优先级元素
- 减小键
二叉堆支持具有以下时间复杂度的上述操作:
- O(1)
- O(登录)
- O(登录)
- O(登录)
自平衡二叉搜索树(如 AVL 树、红黑树等)也可以支持上述具有相同时间复杂度的操作。
- 查找最小值和最大值自然不是 O(1),但可以通过将额外的指针保持为最小值或最大值并在需要时通过插入和删除来更新指针,从而在 O(1) 中轻松实现。通过删除,我们可以通过查找有序的前驱或后继进行更新。
- 插入一个元素自然是O(Logn)
- 去除最大值或最小值也是 O(Logn)
- 减少键可以在 O(Logn) 中通过先删除再插入来完成。有关详细信息,请参阅此内容。
那么为什么优先队列优先使用二叉堆呢?
- 由于二叉堆是使用数组实现的,所以总是有更好的引用局部性,操作对缓存更友好。
- 虽然操作的时间复杂度相同,但二叉搜索树中的常量更高。
- 我们可以在 O(n) 时间内构建一个二叉堆。自平衡 BST 需要 O(nLogn) 时间来构建。
- 二叉堆不需要额外的指针空间。
- 二叉堆更容易实现。
- 有像斐波那契堆这样的二叉堆变体,可以支持在 Θ(1) 时间内插入和减少键
二叉堆总是更好吗?
虽然二叉堆是用于优先队列的,但 BST 也有自己的优势,而且与二叉堆相比,优势列表实际上更大。
- 在自平衡 BST 中搜索一个元素是 O(Logn),也就是二叉堆中的 O(n)。
- 我们可以在 O(n) 时间内按排序顺序打印 BST 的所有元素,但二叉堆需要 O(nLogn) 时间。
- 可以在 O(Logn) 时间内找到 floor 和 ceil。
- 通过用附加字段扩充树,在 O(Logn) 时间内找到第 K 个最大/最小元素。
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。