1. 优先级队列与堆的核心概念
1.1 优先级队列的本质
优先级队列(Priority Queue)是一种特殊的队列,它打破了传统队列先进先出(FIFO)的原则,而是根据元素的优先级进行动态调度。想象一下医院急诊科的场景:不是按照病人挂号顺序就诊,而是根据病情危急程度优先处理重症患者——这就是优先级队列的典型应用场景。
优先级队列的核心特性体现在三个方面:
- 动态优先级管理:元素的优先级可以在运行时动态调整
- 高效访问:能在常数时间O(1)内获取最高/最低优先级元素
- 灵活插入:新元素插入后能自动调整位置,保持优先级顺序
1.2 堆:优先级队列的引擎
堆(Heap)作为优先级队列最高效的实现方式,本质上是一棵完全二叉树,具有以下关键特征:
- 结构特性:除最后一层外,其他层节点都完全填满,且最后一层节点尽可能靠左排列
- 堆序性质:
- 大根堆:每个节点的值都大于或等于其子节点的值
- 小根堆:每个节点的值都小于或等于其子节点的值
这种特殊的结构使得堆能够高效维护元素的优先级顺序。完全二叉树的特性决定了它可以用数组紧凑存储,避免了指针带来的空间开销,同时保持了出色的缓存局部性。
为什么选择完全二叉树?
- 数组存储节省空间(不需要存储指针)
- 通过简单算术运算即可定位父子节点
- 树高始终保持在⌊log₂n⌋,保证操作效率
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 堆的实现细节剖析
2.1 堆的物理存储结构
堆虽然逻辑上是树形结构,但实际存储采用数组形式。对于从0开始索引的数组:
- 父节点索引:(i-1)/2
- 左子节点索引:2i+1
- 右子节点索引:2i+2
这种存储方式带来了显著优势:
- 空间效率:不需要存储指针,空间利用率接近100%
- 访问效率:通过索引直接访问,缓存命中率高
- 遍历方便:数组天然支持顺序访问
java复制// 堆的数组表示示例
int[] maxHeap = {90, 70, 80, 30, 50, 60, 20, 10};
// 对应树形结构:
// 90
// / \
// 70 80
// / \ / \
// 30 50 60 20
// /
//10
2.2 堆的核心操作算法
2.2.1 向下调整(Heapify Down)
当堆顶元素被移除后,需要将最后一个元素移到堆顶,然后执行向下调整:
java复制void shiftDown(int[] heap, int size, int index) {
int largest = index; // 假设当前节点最大
int left = 2 * index + 1;
int right = 2 * index + 2;
// 找出当前节点和左右子节点中的最大值
if
