目录
内部比较器——Comparable.compareTo(E o):
外部比较器——Comparator.compare(T o1, T o2):
铺垫
堆
优先级队列的底层是一个数组,但这个数组又比较特殊——二叉树通过层序遍历存放到该数组的数据。这样存放的方式就是堆这种数据结构。
大根堆:如果在堆中,每个根结点的值大于两个孩子结点的值。(对左右孩子无要求)
小根堆:如果在堆中,每个根结点的值小于两个孩子结点的值。(对左右孩子无要求)
建堆
基本公式
调整为大(小)根堆
在一个一维数组中:
1.对于每一个根结点都要调整为大小根堆。
2.调整时,先要找到较大的孩子
3.然后二者交换,继续向下调整该根结点下面的结点。
4.调整时可能会存在越界的情况,孩子结点可能会越界。
public void createHeap() {
//建大根堆
//对每一个根节点使用向下调整算法
//最后一个孩子 c = length - 1
//c = 2 * p +1
//p = (c - 1)/2
for (int parent = (array.length - 1 -1) / 2; parent >= 0; parent--) {
shiftDown(parent, usedSize);
}
}
//向下调整
private void shiftDown(int parent, int length) {
int child = 2 * parent + 1;
while (child < length) {
//先判断孩子是否越界
if (child < length - 1 && array[child] < array[child + 1]) {
child++;
}
if (array[parent] < array[child]) {
swap(array, parent, child);
parent = child;
child = 2 * parent + 1;
} else {
break;
}
}
}
private void swap(int[] array, int x, int y) {
int tmp = array[x];
array[x] = array[y];
array[y] = tmp;
}
堆的增加
在堆里添加完数据后,应该还要保证堆为大(小)根堆。
1.判断堆是否满了,满了扩容。
2.把添加的数据放到数组的最后面。
3.找到最后一个结点的父亲结点,向上调整。
4.(建大堆)如果父亲结点比孩子结点小,就调整,然后,父亲结点指向孩子结点,重新计算父亲结点,直到最后一个孩子调整完。
5.useSized++
堆的删除
优先级队列,是对于某些数据优先操作的结构,所以很自然的我们要优先对堆顶数据进行删除。
1.判断堆是否为空,为空抛出异常。
2.堆顶数据与最后一个结点进行交换。
3.对堆顶使用一次向下调整即可恢复成大(小)根堆。
4.useSized--
public void pop() {
if (isEmpty()) {
return;
}
swap(array, 0, usedSize - 1);
usedSize--;
shiftDown(0, usedSize);
}
正文:
有了以上概念,我们就能更容易理解PriorityQueue的源码部分的实现。
比较
在生成大小根堆的时候,数据是通过比较大小来排序的。所以PriorityQueue底层必定有比较数据大小的东西。
重写基类的equals()比较方法:
所有的类都是继承Object类的,Object类中就有equals()方法。
重写equals()方法用到这里显然不合适,因为该方法只能比较二者是否想等,无法比较出大小关系。
内部比较器——Comparable<E>.compareTo(E o):
Comparable是JDK提供的泛型接口类,源码如下:
//Compareble是java.lang中的接口类,可以直接使用
public interface Comparable<E> {
// 返回值:
// < 0: 表示 this 指向的对象小于形参 o 指向的对象
// == 0: 表示 this 指向的对象等于形参 o 指向的对象
// > 0: 表示 this 指向的对象大于形参 o 指向的对象
int compareTo(E o);
}
//对象支持自己与自己比较
//实现了该接口的类就可以自动进行排序
//集合通过Collection.sort()排序,数组通过Arrays.sort()进行排序
该方法可以比较出数据的大小,所以我们可以在定义类的时候实现该接口,重写方法就可以比较。
外部比较器——Comparator<T>.compare(T o1, T o2):
//Comparator是java.util 包中的泛型接口类,使用时必须导入对应的包
public interface Comparator<T> {
// 返回值:
// < 0: 表示 o1 指向的对象小于 o2 指向的对象
// == 0: 表示 o1 指向的对象等于 o2 指向的对象
// > 0: 表示 o1 指向的对象等于 o2 指向的对象
int compare(T o1, T o2);
}
//使用Comparator的情况:
//1.没有实现Comparable接口
//2.实现了Comparable接口,但是其中的compareTo方法不符合自己的预期
源码粗略分析
public static void main(String[] args) {
PriorityQueue<Integer> priorityQueue1= new PriorityQueue<>();
priorityQueue1.offer(4);
priorityQueue1.offer(6);
priorityQueue1.offer(3);
System.out.println(priorityQueue1);
}
若是其他引用类型,就不可以这样offer()了,在offer()第二个数据的时候就会抛异常。
class Student {
public String name;
public int age;
public Student (int age) {
this.age = age;
}
}
public class Test {
public static void main(String[] args) {
PriorityQueue<Integer> priorityQueue1= new PriorityQueue<>();
priorityQueue1.offer(4);
priorityQueue1.offer(6);
priorityQueue1.offer(3);
System.out.println(priorityQueue1);
PriorityQueue<Student> priorityQueue2 = new PriorityQueue<>();
priorityQueue2.offer(new Student(20));
priorityQueue2.offer(new Student(22));
}
}
常用构造方法
第一种:什么都不传,那么默认空间大小为11,无比较器
第二种:只传空间大小,此时空间大小为所传的值
第三种:只传比较器,下面详解比较器作用
实现Comparable接口
class Student implements Comparable<Student> {
public String name;
public int age;
public Student (int age) {
this.age = age;
}
@Override
public int compareTo(Student o) {
return this.age - o.age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test {
public static void main(String[] args) {
PriorityQueue<Integer> priorityQueue1= new PriorityQueue<>();
priorityQueue1.offer(4);
priorityQueue1.offer(6);
priorityQueue1.offer(3);
System.out.println(priorityQueue1);
PriorityQueue<Student> priorityQueue2 = new PriorityQueue<>();
priorityQueue2.offer(new Student(20));
priorityQueue2.offer(new Student(22));
priorityQueue2.offer(new Student(23));
System.out.println(priorityQueue2);
}
}
当实现了Comparable接口后,重写CompareTo方法后,此时就可以比较引用类型了。
此时它应该执行到一下源码部分。
实现Comparator接口
class Student {
public String name;
public int age;
public Student (int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
class StuCompare implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o2.age - o1.age;
}
}
public class Test {
public static void main(String[] args) {
PriorityQueue<Integer> priorityQueue1= new PriorityQueue<>();
priorityQueue1.offer(4);
priorityQueue1.offer(6);
priorityQueue1.offer(3);
System.out.println(priorityQueue1);
PriorityQueue<Student> priorityQueue2 = new PriorityQueue<>(new StuCompare());
priorityQueue2.offer(new Student(20));
priorityQueue2.offer(new Student(22));
priorityQueue2.offer(new Student(23));
System.out.println(priorityQueue2);
}
}
通过自定义的比较器,并且通过传入比较器的构造方法后,我们也可以比较。
此时它应该执行到下面的源码部分。
改为大根堆
因为源码默认建小根堆,如果我们想建大根堆,有了以上源码分析,我们不难发现,关键就在比较器上,我们只需要改变比较规则,就可以建立大根堆。
对于基本类型:
比如Integer,我们无法到源码中改变比较规则,所以只能传比较器,这样会优先使用有比较器的方法。
class IntCompare implements Comparator<Integer> {
@Override
public int compare (Integer o1, Integer o2) {
return o2.compareTo(o1);
}
}
public class Test {
public static void main(String[] args) {
PriorityQueue<Integer> priorityQueue1= new PriorityQueue<>(new IntCompare());
priorityQueue1.offer(4);
priorityQueue1.offer(6);
priorityQueue1.offer(3);
System.out.println(priorityQueue1);
}
}
对于引用类型:
若该引用类型实现了Comparable接口,我们直接修改compareTo
若没有,则修改比较器中的compare
class Student implements Comparable<Student> {
public String name;
public int age;
public Student (int age) {
this.age = age;
}
@Override
public int compareTo(Student o) {
return o.age - this.age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test {
public static void main(String[] args) {
PriorityQueue<Student> priorityQueue2 = new PriorityQueue<>();
priorityQueue2.offer(new Student(20));
priorityQueue2.offer(new Student(22));
priorityQueue2.offer(new Student(23));
System.out.println(priorityQueue2);
}
}
class Student {
public String name;
public int age;
public Student (int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
class StuCompare implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o2.age - o1.age;
}
}
public class Test {
public static void main(String[] args) {
PriorityQueue<Student> priorityQueue2 = new PriorityQueue<>(new StuCompare());
priorityQueue2.offer(new Student(20));
priorityQueue2.offer(new Student(22));
priorityQueue2.offer(new Student(23));
System.out.println(priorityQueue2);
}
}
比较器快速写法
class Student {
public String name;
public int age;
public Student (int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test {
public static void main(String[] args) {
//这里直接new了一个比较器传过去了
PriorityQueue<Student> priorityQueue2 = new PriorityQueue<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
});
priorityQueue2.offer(new Student(20));
priorityQueue2.offer(new Student(22));
priorityQueue2.offer(new Student(23));
System.out.println(priorityQueue2);
}
public static void main1(String[] args) {
MyPriorityQueue heap = new MyPriorityQueue();
int[] arr = {27, 15, 19, 18, 28, 34, 65, 49, 25, 37};
heap.initArray(arr);
heap.createHeap();
heap.push(100);
//heap.pop();
PriorityQueue<Integer> priorityQueue1= new PriorityQueue<>();
priorityQueue1.offer(4);
priorityQueue1.offer(6);
priorityQueue1.offer(3);
System.out.println(priorityQueue1);
}
}
有什么错误评论区指出,希望可以帮到你。