PriorityQueue的基本概念等都在上一篇已说明,感兴趣的可以点击 Java中的集合(三)继承Collection的Queue接口 查看

这里主要以PriorityQueue的常用方法的学习

一、PriorityQueue的实现

Java中的集合(四)PriorityQueue常用方法

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

上图中给层序遍历编号,从中可以发现父子节点总有如下的关系:

Java中的集合(四)PriorityQueue常用方法

通过上述三个公式,可以轻易计算出某个节点的父节点以及子节点的下标。这也就是为什么可以直接用数组来存储堆的原因。

PriorityQueue的peek()和element()操作是常数时间add()、offer()、 无参数的remove()以及poll()方法的时间复杂度都是log(N)

二、PriorityQueue常用的方法

Java中的集合(四)PriorityQueue常用方法 

三、常用方法剖析

(一)插入元素:add(E e)和offer(E e)

add(E e)和offer(E e)两者的语义是相同,都是往优先队列中插入元素,只是Queue接口规定了两者对插入失败时采取不同的处理方式。add(E e)方法插入元素失败时会抛出异常,offer(E e)插入元素失败时会返回false,对PriorityQueue而言,两者没有什么区别。

 1 public boolean add(E e) {
 2     return offer(e); // add方法内部调用offer方法
 3 }
 4 public boolean offer(E e) {
 5     if (e == null) // 元素为空的话,抛出NullPointerException异常
 6         throw new NullPointerException();
 7     modCount++;
 8     int i = size;
 9     if (i >= queue.length) // 如果当前用堆表示的数组已经满了,调用grow方法扩容
10         grow(i + 1); // 扩容
11     size = i + 1; // 元素个数+1
12     if (i == 0) // 堆还没有元素的情况
13         queue[0] = e; // 直接给堆顶赋值元素
14     else // 堆中已有元素的情况
15         siftUp(i, e); // 重新调整堆,从下往上调整,因为新增元素是加到最后一个叶子节点
16     return true;
17 }
18 private void siftUp(int k, E x) {
19     if (comparator != null)  // 比较器存在的情况下
20         siftUpUsingComparator(k, x); // 使用比较器调整
21     else // 比较器不存在的情况下
22         siftUpComparable(k, x); // 使用元素自身的比较器调整
23 }
24 private void siftUpUsingComparator(int k, E x) {
25     while (k > 0) { // 一直循环直到父节点还存在
26         int parent = (k - 1) >>> 1; // 找到父节点索引,等同于(k - 1)/ 2
27         Object e = queue[parent]; // 获得父节点元素
28         // 新元素与父元素进行比较,如果满足比较器结果,直接跳出,否则进行调整
29         if (comparator.compare(x, (E) e) >= 0) 
30             break;
31         queue[k] = e; // 进行调整,新位置的元素变成了父元素
32         k = parent; // 新位置索引变成父元素索引,进行递归操作
33     }
34     queue[k] = x; // 新添加的元素添加到堆中
35 }
View Code

相关文章: