PriorityQueue
PriorityQueue是基于堆实现的数据结构,其逻辑结构是一棵完全二叉树,存储结构其实是一个数组。
PriorityQueue,也叫优先级队列,它是不同于先进先出队列的另一种队列。每次从队列中取出的是具有最高优先权的元素。
如果不提供Comparator的话,优先队列中元素默认按自然顺序排列,也就是数字默认是小的在队列头,字符串则按字典序排列。也就是说我们通过设置comparator比较器来定义优先级别。
疑问
先看这段代码:
public class A {
public static void main(String[] args) {
//设置队列的初始长度为10
PriorityQueue<Integer> queue = new PriorityQueue<>(10);
//入队
for(int i=10;i>=5;i--)
queue.offer(i);
//遍历元素
for(Integer i:queue)
System.out.print(i+" ");
}
}
//输出结果:5 7 6 10 8 9
正如上面所说,默认的comparator是按照自然顺序排列,而上面的例子,{10 9 8 7 6 5}数组的元素依次入队,但是出队的顺序却不是 5 6 7 8 9 10,这是为什么呢。
我的疑惑:队列里的元素不是有序的吗?
为什么输出的结果是 5 7 6 10 8 9,而不是 5 6 7 8 9 10???为什么输出结果不是有序的?
改成大顶堆却可以有序输出的:
public class A {
public static void main(String[] args) {
PriorityQueue<Integer> queue = new PriorityQueue<>(10, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
for(int i=10;i>=5;i--)
queue.offer(i);
for(Integer i:queue)
System.out.print(i+" ");
}
}
//输出结果:10 9 8 7 6 5
原因
仔细想了想小顶堆的结构,画一画上面例子的入队过程,总算明白为什么会输出那样的结果了:
小顶堆只是保证根节点不大于左右子节点。但是左右子节点谁大谁小并不能保证。
如果这样解释还是没能想明白,自己模拟一下{10,9,8,7,6,5}的入队过程就明白了。
也就是说,队列里的元素,在物理结构上是一个数组,是无序的。
如何让队列里的元素有序输出
队列在物理结构上是数组,是无序的。但是其逻辑结构,是小顶堆,是有序的。
通过poll方法让堆顶元素出列,然后会进行调整堆操作,调整之后,堆顶又是最小的元素。如此,一直出队,每次出队的都是当前堆里的最小元素(因为出队的是队头元素,而且是小顶堆,因此出队的肯定是最小元素),如此直至队列为空。就能让元素有序输出了。
代码实现:
public class A {
public static void main(String[] args) {
PriorityQueue<Integer> queue = new PriorityQueue<>(10);
for(int i=10;i>=5;i--)
queue.offer(i);
//(此代码根上面代码的区别)通过出列的方式来遍历,而不是直接遍历
while(queue.peek()!=null)
System.out.print(queue.poll()+" ");
}
}
//输出结果:5 6 7 8 9 10
想通之后,也就能想明白为什么上面将优先队列的堆通过自定义comparator比较器设置成大顶堆,不通过出列方式遍历而直接遍历元素,输出的结果就是10 9 8 7 6 5。
最后
其实就是一开始的理解错了。定义中说的明明是“每次从队列中取出的是具有最高优先权的元素。”想到了小顶堆,我就理解成了堆里的元素是有序的。
然而并不完全是。它只是在逻辑结构上是有序的,即根节点大于左右节点。但是从物理结构上看,数组是无序的。只有通过出列的方式遍历,才是有序的。