
PriorityQueue 是一个基于堆实现的无界队列,其不接受 null
值,且不支持 non-comparable 对象。可以通过 Comparator
或者 Comparable
对存储对象进行排序实现小顶堆或者大顶堆,队列会根据储存情况自动扩容。本文会介绍一些 PriorityQueue 的方法以及通过一个算法题展示其用法。
什么是堆?
堆是一种非线性结构,可以把堆看作一个数组,也可以被看作一个完全二叉树,我们可以利用堆的特性找到一个集中的重要元素。
Wiki 中是这么描述堆的:
若母节点的值恒小于等于子节点的值,此堆称为最小堆(min heap);反之,若母节点的值恒大于等于子节点的值,此堆称为最大堆(max heap)。这种数据结构具有以下性质:
- 任意节点小于(或大于)它的所有后裔,最小元(或最大元)在堆的根上(堆序性)。
- 堆总是一棵完全树。即除了最底层,其他层的节点都被元素填满,且最底层尽可能地从左到右填入。
简单来说,堆是一种通过树结构维护的数组,且有特殊有序性的结构。
理解堆的结构
我们提到过堆是通过树结构维护的数组,PriorityQueue 可以通过一个数组来实现:
实际上数组的下标可以通过计算得到:
leftIndex
=parentIndex
* 2 + 1rightIndex
=parentIndex
* 2 + 2parentIndex
= (nodeIndex
- 1) / 2
这里的 leftIndex
和 rightIndex
分别指对应 parentIndex
这个父节点下标的左右两个子节点的数组下标,nodeIndex
理解为当前节点的数组下标,通过他可以推出他的父节点和两个子节点(如存在)的下标。
值得一提的是:
PriorityQueue 的 peek()
和 element()
方法的时间复杂度为 $O(1)$; add()
, offer()
, no-argument remove()
, poll()
方法的时间复杂度都为 $O(\log n)$。
方法解析
add() 和 offer()
add(E e)
和 offer(E e)
的功能相同,都是向队列中插入元素。他们的区别在于:add(E e)
失败时会抛出异常,而 offer(E e)
会返回一个布尔值代表插入成功与否。
我们以 offer()
方法的源码来解释一些 PriorityQueue 的特性。
1 | /** |
前面提到 PriorityQueue 不接受 null
因此在插入 null
时会视为插入失败。而在插入时会根据内外部比较器(Comparable
or Comparator
)进行比较交换。
1 | /** |
在队列空间不足时会进行自动扩容,原理 ArrayList 的 grow()
函数类似,简单来说就是创建一个新的更大的数组并复制过去。
1 | /** |
element() 和 peek()
两者语义相同,功能上都是获取但不弹出队首,区别在于队列为空时 element()
抛出异常,peek()
返回 null
。
remove() 和 poll()
两者语义相同,功能上都是获取并弹出队首,区别在于队列为空时 remove()
抛出异常,poll()
返回 null
。但是由于删除操作会改变队列结构,需要进行调整。以 poll()
为例,见源码:
1 | public E poll() { |
与增操作相同,通过内外部比较器进行比较并更新数组。
remove(Object o)
remove(Object o)
方法用于删除队列中与对象 o
相等的某一个元素(如果有多个相等,只删除一个)。与 remove()
相同,删除元素后需要对结构进行重新排序,源码如下:
1 | public boolean remove(Object o) { |
在重排时,采用的方式还是内外部比较器比较交换的方式,这里就不过多赘述。
算法实战
LeetCode 253.会议室 II,是一道典型的使用 PriorityQueue 求解的问题,由于题目为会员题,这里就不贴出题目了,下面先上代码,再来看看解题思路:
1 | class Solution { |
我们在实例化时为 PriorityQueue 创建了一个外部比较器,使之结构为小顶堆(Java 中的优先队列默认就是小顶堆,这里创建了内部匿名类是为了展示写法)。
我们可以利用小顶堆的根节点为数组中最小元素的特性,如果新的会议开始时间比 PriorityQueue 的根节点小则说明需要新的房间开会,而如果大于则说明当前房间在开会前已经结束最新的会议,那么可以继续使用这个房间。最后输出堆的规模,也就是开了房间的数量。
- Post title:优先级队列 PriorityQueue
- Post author:Lynch Lee
- Create time:2021-05-10 14:57:46
- Post link:https://neeomaclynch.github.io/2021/05/10/优先级队列-PriorityQueue/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.