
🌸雨落在了我的手上:个人主页
🐟个人仓库:Gitee仓库
❄️个人专栏:<<JaveSe>> <<C语言>> <<C语言数据结构>>**
🔥🔥🔥 人生格言:无人扶我青云志,我自踏雪至山巅
🎬 博主简介:

目录
一:优先级队列
1.概念
前⾯介绍过队列,队列是⼀种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先 级,⼀般出队列时,可能需要优先级⾼的元素先出队列,该中场景下,使⽤队列显然不合适,⽐如: 在⼿机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话;初中那会班主任排座位 时可能会让成绩好的同学先挑座位。
在这种情况下,数据结构应该提供两个最基本的操作,⼀个是返回最⾼优先级对象,⼀个是添加新的 对象。这种数据结构就是优先级队列(PriorityQueue)
二:优先级队列的模拟实现
JDK1.8中的PriorityQueue底层使⽤了堆这种数据结构,⽽堆实际就是在完全⼆叉树的基础上进⾏了 ⼀些调整。
1.堆的概念
如果有⼀个关键码的集合K={k0,k1,k2,...,kn-1},把它的所有元素按完全⼆叉树的顺序存储⽅ 式存储在⼀个⼀维数组中,并满⾜:Ki=K2i+1且Ki>=K2i+2)i=0, 1,2...,则称为⼩堆(或⼤堆)。将根节点最⼤的堆叫做最⼤堆或⼤根堆,根节点最⼩的堆叫做最⼩堆或 ⼩根堆。
堆的性质:
- 堆中某个节点的值总是不⼤于或不⼩于其⽗节点的值;
- 堆总是⼀棵完全⼆叉树。

2.堆的存储⽅式
从堆的概念可知,堆是⼀棵完全⼆叉树,因此可以层序的规则采⽤顺序的⽅式来⾼效存储

注意:对于⾮完全⼆叉树,则不适合使⽤顺序⽅式进⾏存储,因为为了能够还原⼆叉树,空间中必须 要存储空节点,就会导致空间利⽤率⽐较低。
将元素存储到数组中后,可以根据⼆叉树章节的性质5对树进⾏还原。假设i为节点在数组中的下标,则 有:
- 如果i为0,则i表⽰的节点为根节点,否则i节点的双亲节点为(i-1)/2
- 如果2*i+1⼩于节点个数,则节点i的左孩⼦下标为2*i+1,否则没有左孩⼦
- 如果2*i+2⼩于节点个数,则节点i的右孩⼦下标为2*i+2,否则没有右孩⼦
3.堆的创建
1.堆向下调整
对于集合{27,15,19,18,28,34,65,49,25,37 }中的数据,如果将其创建成堆呢?

仔细观察上图后发现:根节点的左右⼦树已经完全满⾜堆的性质,因此只需将根节点向下调整好即 可。
向下过程(以⼩堆为例):
1.让parent标记需要调整的节点,child标记parent的左孩⼦(注意:parent如果有孩⼦⼀定先是有左 孩⼦)
2.如果parent的左孩⼦存在,即:child<size, 进行以下操作,直到parent的左孩子不存在
- parent右孩⼦是否存在,存在找到左右孩⼦中最⼩的孩⼦,让child进⾏标
- 将parent与较⼩的孩⼦child⽐较
如果:
- parent⼩于较⼩的孩⼦child,调整结束
- 否则:交换parent与较⼩的孩⼦child,交换完成之后,parent中⼤的元素向下移动,可能 导致⼦树不满⾜对的性质,因此需要继续向下调整,即parent=child;child= parent*2+1; 然后继续2。

核心代码:
java
/**
* 向下调整(小顶堆):将指定节点与其较小的孩子交换,直到满足堆性质
* @param array 待调整的数组
* @param parent 当前需要调整的节点索引
*/
public void shiftDown(int[] array, int parent) {
int size = array.length;
int child = 2 * parent + 1; // 先标记左孩子
while (child < size) {
// 如果右孩子存在且比左孩子小,则 child 指向右孩子(较小者)
if (child + 1 < size && array[child + 1] < array[child]) {
child++;
}
// 如果父节点已经不大于孩子,说明以 parent 为根的子树已满足小顶堆
if (array[parent] <= array[child]) {
break;
}
// 交换父节点与较小的孩子
int temp = array[parent];
array[parent] = array[child];
array[child] = temp;
// 继续向下调整被交换的孩子节点
parent = child;
child = 2 * parent + 1;
}
}
注意:在调整以parent为根的⼆叉树时,必须要满⾜parent的左⼦树和右⼦树已经是堆了才可以向下调整。
2.堆的创建
那对于普通的序列{1,5,3,8,7,6},即根节点的左右⼦树不满⾜堆的特性,⼜该如何调整呢?

核心代码:
java
public static void createHeap(int[] array) {
// 找倒数第一个非叶子节点,从该节点位置开始往前一直到根节点
//遇到一个节点,应用向下调整
int root = ((array.length - 2) >> 1);
for (; root >= 0; root--) {
shiftDown(array, root);
}
}
4.堆的插⼊与删除
1.堆的插⼊
堆的插⼊总共需要两个步骤:
- 先将元素放⼊到底层空间中(注意:空间不够时需要扩容)
- 将最后新插⼊的节点向上调整,直到满⾜堆的性质

核心代码(向上调整):
java
public void shiftUp(int[] array, int child) {
int parent = (child - 1) / 2;
while (child > 0) {
// 如果父节点 >= 孩子,满足大顶堆,结束
if (array[parent] >= array[child]) {
break;
}
// 将双亲与孩⼦节点进⾏交换
int temp = array[parent];
array[parent] = array[child];
array[child] = temp;
// ⼩的元素向下移动,可能到值⼦树不满⾜对的性质,因此需要继续向上调增
child = parent;
parent = (child - 1) / 2;
}
}
2.堆的删除
注意:堆的删除⼀定删除的是堆顶元素。具体如下:
- 将堆顶元素对堆中最后⼀个元素交换
- 将堆中有效数据个数减少⼀个
- 对堆顶元素进⾏向下调整

5.⽤堆模拟实现优先级队列
这里的向下和向上调整的代码在上面已经写过了,这里只写核心代码,辅助代码就不写了
核心代码:
java
public class MyPriorityQueue {
// ---------- 成员变量 ----------
// 演示作用,固定容量为 100,不再考虑扩容
private int[] array = new int[100];
private int size = 0; // 当前有效元素个数
// ---------- 核心公共方法 ----------
/**
* 插入元素(入队)
* @param e 要插入的元素值
*/
public void offer(int e) {
// 先检查队列是否已满(防止数组越界)
if (size == array.length) {
throw new RuntimeException("队列已满,无法插入");
}
// 1. 将新元素放在数组末尾,同时 size 加 1
array[size++] = e;
// 2. 对新元素进行向上调整,参数是刚刚插入的位置(size-1)
shiftUp(size - 1);
}
/**
* 弹出并返回堆顶元素(最大值)
* @return 原堆顶的值
*/
public int poll() {
// 先检查队列是否为空
if (size == 0) {
throw new RuntimeException("队列为空,无法弹出");
}
// 1. 记录堆顶的值(用于返回)
int oldValue = array[0];
// 2. 用最后一个元素覆盖堆顶,同时 size 减 1(--size 先减后使用)
array[0] = array[--size];
// 3. 对新的堆顶进行向下调整(保持堆性质)
shiftDown(0);
// 4. 返回原堆顶值
return oldValue;
}
/**
* 查看堆顶元素(不弹出)
* @return 当前最大值
*/
public int peek() {
// 检查队列是否为空
if (size == 0) {
throw new RuntimeException("队列为空,无法查看");
}
// 直接返回堆顶元素
return array[0];
}
}
三:常⽤接⼝介绍
1.PriorityQueue的特性
Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列, PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的,本⽂主要介绍 PriorityQueue。

关于PriorityQueue的使⽤要注意:
使⽤时必须导⼊PriorityQueue所在的包,即:
java
import java.util.PriorityQueue;
- PriorityQueue中放置的元素必须要能够⽐较⼤⼩,不能插⼊⽆法⽐较⼤⼩的对象,否则会抛出 ClassCastException异常
- 不能插⼊null对象,否则会抛出NullPointerException
- 没有容量限制,可以插⼊任意多个元素,其内部可以⾃动扩容
- PriorityQueue底层使⽤了堆数据结构
- PriorityQueue默认情况下是⼩堆---即每次获取到的元素都是最⼩的元素
2.PriorityQueue常⽤接⼝介绍
1.优先级队列的构造
此处只是列出了PriorityQueue中常⻅的⼏种构造⽅式,其他的学⽣们可以参考帮助⽂档

2.插⼊/删除/获取优先级最⾼的元素
以上就是全部内容了!!!!!
