目录
在二叉树中经常使用链式存储,而堆也是二叉树只不过它是顺序存储的.
在java中提供了关于堆的集合 PriorityQueue 优先级队列.
在了解优先级队列之前,先来了解一下堆.
堆
概念
![](https://img-blog.csdnimg.cn/8c2c514489c44056bc33165f5f224afd.png)
![](https://img-blog.csdnimg.cn/e73e05c640414dad9c082edb28b4089d.png)
如何创建一个堆
1.向下调整
向下调整 : 时间复杂度 : O(logn) 空间复杂度 : O(1)
通过向下调整建堆的时间复杂度 : O(n) 空间复杂度 : O(1)
//建堆(大根堆)
public void createHeap(int[] array) {
//array.length - 1 是数组有效数字的最后一个下标
//(arrat.length - 1 - 1) / 2 这个公式得到的就是父亲下标
for (int parent = (array.length - 1 - 1) / 2; parent >= 0; parent--) {
shiftDown(array,parent,array.length - 1);
}
}
//向下调整
public void shiftDown(int[] array, int parent, int end) {
//得到父亲节点的左孩子的位置
int child = parent * 2 + 1;
//判断孩子下标有没有越界
while (child <= end) {
//判断有没有右孩子 并且 右孩子的值有没有左孩子大
if (child + 1 <= end && array[child + 1] > array[child]) {
child++;
}
//如果父亲下标的值小于孩子下标的值就进行交换 否则就退出循环
//并且让parent = child 进入下一轮循环
if (array[parent] < array[child]) {
swap(array,parent,child);
parent = child;
child = parent * 2 + 1;
} else {
break;
}
}
}
//交换
public void swap(int[] array, int x, int y) {
int temp = array[x];
array[x] = array[y];
array[y] = temp;
}
2 . 向上调整
//使用向上调整建堆
public void createHeap1(int[] array) {
for (int child = 1; child < array.length; child++) {
shiftUp(array,child);
}
}
//向上调整
public void shiftUp(int[] array, int child) {
int parent = (child - 1) / 2;
while (child > 0) {
if (array[child] > array[parent]) {
swap(array,child,parent);
child = parent;
parent = (child - 1) / 2;
} else {
break;
}
}
}
//交换
public void swap(int[] array, int x, int y) {
int temp = array[x];
array[x] = array[y];
array[y] = temp;
}
向上调整的时间复杂度 : O(logn)
通过向上调整建堆的时间复杂度 : O(n*logn)
堆的删除和插入
插入 : 直接让插入进来的数也就是最后一个下标向上调整.
public void insertNum(int[] array, int num) {
int length = array.length;
//在这里假设数组没满 就把Num放到length位置
array[length] = num;
shiftUp(array,length);
}
删除 : 思路 : 让堆顶元素(0下标) 与 数组的最后一个有效元素的下标进行交换.
交换完成后最后一个数就不再列入有效数字中
再让0下标进行向下调整
//堆的删除
public void poll(int[] array) {
//这里要注意 假设array.length - 1就是数组的有效数字的最后一个
swap(array,0,array.length - 1);
shiftDown(array,0,array.length - 2);
}
优先级队列
1.概念
2. 关于PriorityQueue的使用注意 :
1.使用时必须导入PriorityQueue所在的包 :
import java.util.PriorityQueue;
![](https://img-blog.csdnimg.cn/71280fd97e6b49f0a834d254f5aa6620.png)
3. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容.
4.插入和删除元素的时间复杂度为O(logn)
5.PriorityQueue底层使用了堆数据结构 .
6.PriorityQueue默认情况下是小堆 ---- >即每次获取到的元素都是最小的元素
class Student {
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Test {
public static void main(String[] args) {
Queue<Student> priorityQueue = new PriorityQueue<>();
Student stu1 = new Student("张三",12);
Student stu2 = new Student("李四",22);
priorityQueue.offer(stu1);
priorityQueue.offer(stu2);
}
}
![](https://img-blog.csdnimg.cn/802e9970c18349d08f278b675350aebc.png)
![](https://img-blog.csdnimg.cn/06c5340c89f04fae961dc09ad75d1801.png)
在PriorityQueue中如果没有传比较器,在要被比较的类型实现Comparable接口的前提下,那么它就会自动通过compareTo()方法来进行比较.
对象的比较
1. 判断两个对象相不相同 -------->equals
public static void main(String[] args) {
Student stu1 = new Student("张三",12);
Student stu2 = new Student("张三",12);
System.out.println(stu1 == stu2);
}
结果为 false 因为 "==" 比较的是两个引用数据类型的地址,一般的我们要去比较两个引用相不相同的话, 就要通过equals方法. 如果引用数据类型没有实现equals方法那么就需要我们自己去重写equals方法.
class Student implements Comparable<Student>{
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public static void main(String[] args) {
Student stu1 = new Student("张三",12);
Student stu2 = new Student("张三",12);
System.out.println(stu1.equals(stu2));
}
2. 判断两个对象谁大谁小 ----------> 要么实现comparable<>接口 要么使用comparator<>构造器
对于引用类型直接去使用大于小于号比较也是不行的.
需要通过实现comparable<>接口 或者使用comparator<>构造器
class Student implements Comparable<Student>{
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Student o) {
return this.age - o.age;
}
}
public static void main(String[] args) {
Student stu1 = new Student("张三",22);
Student stu2 = new Student("李四",12);
System.out.println(stu1.compareTo(stu2));
}
但这种比较方法它对类的侵入性太强了!
相比之下,comparator()比较器就会更好一点!
class Student implements Comparable<Student>{
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Student o) {
return this.age - o.age;
}
}
//用来比较年龄的比较器
class CompareAge implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
}
public static void main(String[] args) {
Student stu1 = new Student("张三",22);
Student stu2 = new Student("李四",12);
CompareAge compareAge = new CompareAge();
System.out.println(compareAge.compare(stu1, stu2));
}
这就是对象的比较.
然后再回到PriorityQueue
class Student implements Comparable<Student>{
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Student o) {
return this.age - o.age;
}
}
//用来比较年龄的比较器
class CompareAge implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
}
public class Test {
public static void main(String[] args) {
CompareAge compareAge = new CompareAge();
//传进入一个比较器
Queue<Student> priorityQueue = new PriorityQueue<>(compareAge);
Student stu1 = new Student("张三",22);
Student stu2 = new Student("李四",12);
priorityQueue.offer(stu1);
priorityQueue.offer(stu2);
System.out.println(priorityQueue.poll().name);
}
}
对于PriorityQueue 它里面是一个小根堆 如何变成大根堆呢?
或者也可以使用匿名内部类来实现构造器!
Queue<Student> pri = new PriorityQueue<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o2.age - o1.age;
}
});
希望可以帮到大家~~~~~🎉🎉🎉