目录
个人笔记分享
优先级队列(堆)
底层实现是一个完全二叉树
小根堆:根节点比左右孩子都小
大根堆:根节点比左右孩子都大
向下调整的过程:
1.从最后一颗子树开始调整的
2.找到左右孩子的最大值和根节点进行比较,如果比根节点大,那么就交换
3.如果能够知道子树的根节点下标,那么下一棵子树就是当前根节点下标-1
4.一直调整到0下标这棵树停止
建堆:向下调整
插入:向上调整
删除:先将堆顶元素和最后一个元素进行交换,然后us--,再进行向下调整
//堆的删除一定删除的是堆顶元素
PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的
当我们实例化一个PriorityQueue对象之后,其实默认是一个小根堆
优先级队列扩容方式:
如果容量小于64时,进行2倍扩容
如果容量大于等于64时,进行1.5倍扩容
以上是我认为优先级队列中需要重点注意的一些知识点!
底层源码剖析
import java.util.Arrays;
// 堆的自我实现 - 创建堆 + 插入数据 + 删除数据 + 返回堆顶元素 + 判断是否为空
public class MyPriorityQueue {
public int[] elem; // 存储堆元素的数组
public int useSize; // 当前堆中元素的个数
// 初始化堆
public MyPriorityQueue(int[] array) {
elem = new int[11]; // 初始化堆的容量
for (int i = 0; i < array.length; i++) {
elem[i] = array[i]; // 将传入的数组元素放入堆中
useSize++; // 更新堆中元素的个数
}
BigHeap(array, useSize); // 创建大根堆
}
// 整体创建堆
public void BigHeap(int[] array, int useSize) {
// 从最后一个非叶子节点开始向上调整
for (int parent = (useSize - 1 - 1) / 2; parent >= 0; parent--) {
shiftDown(parent, useSize); // 对每个非叶子节点进行下沉操作
}
}
// 下沉操作
private void shiftDown(int root, int useSize) {
int child = 2 * root + 1; // 左子节点索引
while (child < useSize) { // 遍历堆
// 如果右子节点存在且右子节点的值大于左子节点的值,选择右子节点
if (child + 1 < useSize && elem[child] < elem[child + 1]) {
child++;
}
// 如果当前节点的值小于子节点的值,交换它们
if (elem[root] < elem[child]) {
swap(root, child);
root = child; // 更新根节点为被交换的子节点
child = 2 * root + 1; // 更新左子节点的索引
} else {
break; // 如果根节点的值不小于任何子节点,退出循环
}
}
}
// 在堆中插入元素
private boolean isFull() {
return useSize == elem.length; // 判断堆是否满了
}
public void offer(int val) {
if (isFull()) {
elem = Arrays.copyOf(elem, 2 * elem.length); // 扩容
}
elem[useSize] = val; // 将新元素放入堆的末尾
shiftUp(useSize); // 上浮操作调整堆
useSize++; // 更新堆中元素的个数
}
// 上浮操作
private void shiftUp(int child) {
int parent = (child - 1) / 2; // 计算父节点的索引
while (parent >= 0) {
// 如果当前节点的值大于父节点的值,交换它们
if (elem[parent] < elem[child]) {
swap(parent, child);
child = parent; // 更新子节点为被交换的父节点
parent = (child - 1) / 2; // 更新父节点的索引
} else {
break; // 如果当前节点的值不大于父节点的值,退出循环
}
}
}
// 删除堆中元素
public void poll() {
if (isEmpty()) {
throw new RuntimeException("堆中元素为空!"); // 如果堆为空,抛出异常
}
swap(0, useSize - 1); // 将堆顶元素与最后一个元素交换
useSize--; // 更新堆中元素的个数
shiftDown(0, useSize); // 对堆顶元素进行下沉操作
}
// 获取堆顶元素
public int peek() {
if (isEmpty()) {
throw new RuntimeException("堆中元素为空!"); // 如果堆为空,抛出异常
}
return elem[0]; // 返回堆顶元素
}
// 判断堆是否为空
private boolean isEmpty() {
return useSize == 0; // 如果堆中没有元素,返回 true
}
// 交换数组中的两个元素
private void swap(int pos1, int pos2) {
int temp = elem[pos1];
elem[pos1] = elem[pos2];
elem[pos2] = temp;
}
}
优先级队列中的函数
函数名 | 功能 |
---|---|
boolean offer(E e) | 插入元素e,插入成功返回true,如果e对象为空,抛出NullPointerException异常,时间复杂度O(logN),注意:空间不够时候会进行扩容 |
E peek() | 获取优先级最高的元素,如果优先级队列为空,返回null |
E poll() | 移除优先级最高的元素并返回,如果优先级队列为空,返回null |
int size() | 获取有效元素的个数 |
void clear() | 清空 |
boolean isEmpty() | 检测优先级队列是否为空,空返回true |
至此,您对数据结构中优先级队列的知识已经有基本的了解了,可以开始尝试去使用它了