优先级队列与堆(大根堆详解 + Java实现)
本文讲解优先级队列、堆的底层原理、下标映射规则、建堆、增删改操作,配套 Java 完整代码,适合数据结构复习
一、优先级队列
1. 优先级队列概念
前面学习的普通队列遵循先进先出(FIFO)。
而优先级队列是一种特殊队列:元素出队的依据不是入队先后顺序,而是元素的优先级,优先级高的元素先出队,简单说:谁优先级高,谁先出去。
为什么需要优先级队列?
某些场景下处理的数据带有优先级,需要优先处理高优先级的数据,普通 FIFO 队列就无法满足需求。
举个生活例子:手机正在玩游戏时,有电话打入,操作系统会优先处理电话这个高优先级任务。
这类场景下,数据结构核心需要两个功能:
- 获取 / 返回当前最高优先级的对象
- 添加新的对象
具备这两个能力的抽象数据结构,就是优先级队列(Priority Queue)。
Java 中的 PriorityQueue
✅ 物理存储:数组(内存中真实存放数据)
✅ 逻辑结构:完全二叉树(只是逻辑抽象,没有节点、left/right 指针)
✅ 抽象类型:优先级队列(ADT,对外暴露功能)
重点:Java 自带的PriorityQueue底层是数组,逻辑上映射为完全二叉树,默认实现为小根堆,依靠堆的特性实现优先级队列。
二、堆
1. 堆的概念
⚠️ 区分:优先级队列是抽象数据类型,规定 "高优先级先出" 的行为;堆是存储结构。一般用堆实现优先级队列,但是优先级队列 ≠ 堆,不要混淆!
堆:物理上使用数组存储,逻辑上看成一棵完全二叉树,依靠数组下标公式维护父子关系。
2. 堆的两大性质
- 结构性质:堆一定是完全二叉树
- 堆序性质:分为小根堆、大根堆
-
小根堆:任意父节点的值 ≤ 左右子节点的值;堆顶(根)是整个堆最小元素
-

-
大根堆:任意父节点的值 ≥ 左右子节点的值;堆顶(根)是整个堆最大元素

3. 堆的存储方式
堆逻辑上是完全二叉树,不需要链表节点指针,适合顺序存储(数组存储)。
将元素存入数组后,可以利用完全二叉树的性质由数组还原这棵逻辑上的二叉树。设节点在数组中的下标为 i:
- 若 i=0,该节点是根节点;否则节点 i 的双亲下标为 (i-1)/2(整数除法向下取整)
- 若 2i+1 < 节点总数,则节点 i 的左孩子下标为 2i+1,否则无左孩子
- 若 2i+2 < 节点总数,则节点 i 的右孩子下标为 2i+2,否则无右孩子
4. 堆的创建(Java)
示例数据集合:{ 27,15,19,18,28,34,65,49,25,37 }
Java 自带 PriorityQueue 默认是小根堆,下面我们手动实现大根堆。
建堆核心思路:从最后一个非叶子节点向前遍历,依次执行向下调整(下沉 shiftDown)
向下过程 (以大堆(大根堆)为例):
让 parent 标记需要调整的节点,child 标记 parent 的左孩子 (注意:parent 如果有孩子一定先是有左孩子)
如果 parent 的左孩子存在,即:child < size, 进行以下操作,直到 parent 的左孩子不存在
parent 右孩子是否存在,存在找到左右孩子中最大的孩子,让 child 进行标记
将 parent 与较大的孩子 child 比较,如果:
parent 大于等于较大的孩子 child,调整结束
否则:交换 parent 与较大的孩子 child,交换完成之后,parent 中小的元素向下移动,可能导致子树不满足堆的性质,因此需要继续向下调整,即 parent = child;child = parent*2+1; 然后继续。
循环完成所有非叶子节点下沉之后,整个数组就满足大根堆性质。
三、堆的其他操作(增删改查)
1. 堆的插入(增)
插入思路:
把新元素放在完全二叉树最后的位置(数组usedSize下标处),然后执行向上调整(上浮 shiftUp),向上比较交换,维持大根堆性质。
2. 堆的删除(删)
堆默认只删除堆顶元素
删除思路:
交换堆顶元素和最后一个有效元素,有效元素数量减一,然后对新的堆顶执行向下调整(下沉 shiftDown)。
3. 堆的修改(改)
修改堆顶元素:更新堆顶值之后,从根节点开始向下调整。
拓展:如果修改任意下标位置元素:大根堆中,新值更大 → 上浮;新值更小 → 下沉
4. 堆的查看(查)
直接返回堆顶elem0;操作前先判断堆是否为空。