- 🎥 个人主页:Dikz12
- 📕格言:那些在暗处执拗生长的花,终有一日会馥郁传香
- 欢迎大家👍点赞✍评论⭐收藏
目录
1.优先级队列
2.优先级队列的模拟实现
2.1 堆的概念
2.2 堆的创建
2.3 堆的插入和删除
2.4 建堆的时间复杂度
3.PriorityQueue接口介绍
1.优先级队列
有些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列,该中场景下,使用队列显然不合适,比如:初中那会班主任排座位时可能会让成绩好的同学先挑座位。
2.优先级队列的模拟实现
优先级队列 ( Priority Queue)底层使用了堆这种数据结构,而堆实际上就是对完全二叉树进行了一些调整。
2.1 堆的概念
堆的性质:
- 堆中某个节点的值总是不大于或不小于其父(根)节点的值;
- 堆是一颗完全二叉树
所以,堆可以分为大根堆和小根堆,也都是完全二叉树。
- 小根堆:根节点 比 左右孩子都小;左右孩子谁最小没有关系,只考虑根和左右孩子的关系
- 大根堆: 根节点 比 左右孩子都大
2.2 堆的创建
对于集合{ 27,15,19,18,28,34,65,49,25,37 }中的数据,如果将其创建成堆呢?
可以采用向下调整方式,创建大/小根堆:
创建大根堆 :
整个调整过程:
1.从最后一颗子树开始调整
2.找到左右孩子的最大值 和 根节点进行比较,如果比根节点大,那么就交换
3.如果能够知道子树的根节点下标,那么下一颗子树的位置就是当前根节点下标 -1
4.一直调整到下标为0结束
关键问题:
1.每棵子树调整的时候,什么时候结束?
就是孩子的下标要小于length
2.最后一颗子树的根节点下标如何确定?
(9-1) / 2 => (length -1) / 2
public void createBigHeap() {
for (int parent = (usedSize - 1 - 1) / 2; parent >= 0; parent--) {
siftDown(parent,usedSize);
}
}
public void siftDown(int parent, int end) {
int child = parent * 2 + 1;
while (child < end) {
if (child + 1 < usedSize && elem[child] < elem[child + 1]) {
child++;
}
//开始调整 此时child下标,一定是最大值
if (elem[child] > elem[parent]) {
//交换
swap(child, parent);
//交换之后,验证下面是否还满足大根堆
parent = child;
child = parent * 2 + 1;
} else {
//都满足
break;
}
}
}
public void swap(int i,int j) {
int tmp = elem[i];
elem[i] = elem[j];
elem[j] = tmp;
}
2.3 堆的插入和删除
插入:
1.判断是否需要扩容
2.把插入的值放到最后
3.开始向上调整
关键问题:
什么时候调整结束?
4 到---> 1 : (p - 1)/ 2 ,在 c = p, 由此推-> 当 c == 0 或者 p < 0 结束 。
插入后:
//插入
public void offer(int val) {
//1.是否需要扩容
if (isFull()) {
this.elem = Arrays.copyOf(elem,2*elem.length);
}
//2.向上调整
elem[usedSize] = val;
siftUp(usedSize);
usedSize++;
}
public void siftUp(int child) {
int parent = (child - 1) / 2;
while (child > 0) {
if (elem[child] > elem[parent]) {
swap(child,parent);
child = parent;
parent = (child - 1) / 2;
} else {
break;
}
}
}
public boolean isFull() {
return usedSize == elem.length;
}
删除:
1.交换0 和 最后一个下标的值,uesdSize--
2.在向下调整就行
//删除
public int poll() {
if (empty()) {
return -1;
}
int delVal = elem[0];
swap(0,usedSize-1);
usedSize--;
siftDown(0,usedSize);
return delVal;
}
public boolean empty() {
return usedSize == 0;
}
2.4 建堆的时间复杂度
因此,堆向下调整建堆的时间复杂度为O(n)。
3.PriorityQueue接口介绍
注意:
1. 使用时必须导入PriorityQueue所在的包,即:
import java.util.PriorityQueue;
2. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出
ClassCastException异常。
3. 不能插入null对象,否则会抛出NullPointerException。
4. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容。
5. 插入和删除元素的时间复杂度为O(log2N)。
6. PriorityQueue底层使用了堆数据结构。
7. PriorityQueue默认情况下是小堆---即每次获取到的元素都是最小的元素。