前言
这篇博客主要内容是解决为什么优先级队列默认是小根堆的问题.
我们总是听老师讲,优先级队列默认是小根堆,大家思考过为什么是小根堆?而不是大根堆,是小根堆的话,它又是如何实现的呢?.接下来就让我们来探索一下.
可以看到下面的代码,我将两个学生对象放入优先级队列中,然后调试.
import java.util.PriorityQueue;
class Student implements Comparable<Student>{
int age;
public Student(int age){
this.age = age;
}
@Override
public int compareTo(Student o) {
return this.age - o.age;
}
}
class Test {
public static void main(String[] args) {
PriorityQueue<Student> queue = new PriorityQueue<>();
queue.offer(new Student(5));
queue.offer(new Student(10));
System.out.println("123");
}
}
可以看到上图调试的结果,第一个是5,第二个是10,说明是小根堆.
程序都是按照顺序进行的嘛,我就从main函数第一行代码开始分析.有下图可以看到,在我们实例化优先级队列时 DEFAULT_INITIAL_CAPACITY = 11 进入到构造方法中的方法时,可以看到initialCapacity = 11,不满足if条件,程序往下执行,初始化了queue这个数组,继续往下执行,比较器还是null, main函数的第一行代码就走完了. 这时候就有人想问了,初始化queue这个数组有什么用呢? 请听下面分解.
然后就是第二行代码. 传入一个Student对象即e就是Student对象, modCount++不用管他,根据上面的分析可以知道,queue.length = 11, 所以不满足if中的条件. 右图可以看到源码中size默认是0,i就等于零.size在这个方法中变成了1,i = 0满足if中条件,queue数组0下标的值就是 age = 5的Student对象. 最后返回true结束方法. 第二行代码,主要是将Student对象放入queue数组的0下标.
下面来看第三行代码.按照正常逻辑来看,还是一样的结果,第一次我也搞错了,以为还是走一样的代码,程序就结束了,可是并没有结束.第一次调用offer方法的时候,size的值改变了,size = 1; 当第二次调用offer方法时,i != 0,会调用siftUp(i, e)方法,不满足条件,走else里面的方法.
走完siftUpComparable(int k, E x)方法,其实就干了一件事情,不满足小根堆就交换.使源码中的queue数组满足小根堆的格式.
好了,到了这里,为什么优先级队列默认是小根堆的问题就解决了,博客还没有写完.
那就有人要问了,可不可以把它改成大根堆?首先我们要明白一件事情,源码是改不了的,那就只能从别的地方入手,从哪里入手呢?分析一下:我们在调用offer这个方法的时候,先是将age = 10的学生对象放入queue数组中,然后将age = 5的学生对象放入queue数组中,这个时候已经是大根堆了,只是被改成了小根堆,那就可以这样思考,可不可以不改变数组queue 0下标和1下标的内容?
答案是可以的.只需要将比较条件修改一下就行了,就会进入if,循环结束,不交换,key自己赋值给自己,内容没变.这就达到了大根堆的目的.
if (key.compareTo((E) e) >= 0) break;
改了比较的内容,调试结果如下图,就变成了大根堆了.
好了以上就是全部内容了,假如对你有帮助的话,不要忘了关注 点赞 收藏哦!!!