PriorityQueue的基本概念等都在上一篇已说明,感兴趣的可以点击 Java中的集合(三)继承Collection的Queue接口 查看
这里主要以PriorityQueue的常用方法的学习
一、PriorityQueue的实现
从上图中给层序遍历编号,从中可以发现父子节点总有如下的关系:
通过上述三个公式,可以轻易计算出某个节点的父节点以及子节点的下标。这也就是为什么可以直接用数组来存储堆的原因。
PriorityQueue的peek()和element()操作是常数时间,add()、offer()、 无参数的remove()以及poll()方法的时间复杂度都是log(N)。
二、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 }