堆的基本概念
• 最小堆 :每个父节点的值 ≤ 子节点的值,堆顶元素是整个堆的最小值。 • 存储结构:用数组实现,索引从 0 开始,父子节点关系:
- 父节点索引 =
(子节点索引 - 1) / 2
- 左子节点索引 =
父节点索引 * 2 + 1
- 右子节点索引 =
父节点索引 * 2 + 2
代码结构总览
手动堆的实现围绕以下四个核心方法:
insert(int num)
:插入元素并调整堆结构removeMin()
:移除堆顶元素并调整堆结构swim(int index)
:上浮操作,用于插入元素时调整sink(int index)
:下沉操作,用于移除元素时调整
分步详解
步骤 1:初始化堆
java
heap = new int[k]; // 创建一个大小为k的数组
size = 0; // 当前堆中元素个数
步骤 2:插入元素 (insert
方法)
目标:将新元素插入堆中,并维持最小堆性质。
操作流程:
- 将元素放到数组末尾 :
heap[size] = num;
- 执行上浮 (
swim
):与父节点比较,如果更小则交换位置,直到父节点 ≤ 当前节点。
示例 (插入元素 3
):
text
初始堆: [2,5,7](父-子关系正确)
插入 3:
-> 先放到末尾 [2,5,7,3]
-> 3的父节点是7,3 < 7 → 交换 → [2,5,3,7]
-> 3的新父节点是5,3 < 5 → 交换 → [2,3,5,7]
最终堆结构正确。
步骤 3:移除堆顶元素 (removeMin
方法)
目标:移除堆顶(最小值)后,将最后一个元素移到堆顶,并下沉调整。
操作流程:
- 将堆顶与最后一个元素交换 :
heap[0] = heap[size-1];
- 执行下沉 (
sink
):与较小的子节点比较,如果更大则交换,直到两个子节点都 ≥ 当前节点。
示例 (移除堆顶 2
):
text
初始堆: [2,3,5,7]
移除堆顶:
-> 将最后一个元素7放到堆顶 → [7,3,5]
-> 7的左子节点3,右子节点5 → 较小的子节点是3
-> 7 > 3 → 交换 → [3,7,5]
-> 7的新左子节点无,右子节点无 → 停止
最终堆结构正确。
核心方法详解
1. 上浮操作 swim(int index)
java
private void swim(int index) {
while (index > 0 && heap[index] < heap[(index - 1) / 2]) {
swap(index, (index - 1) / 2); // 与父节点交换
index = (index - 1) / 2; // 更新当前索引
}
}
作用:将新插入的元素从下往上调整到正确位置。
流程图:
text
插入元素 → 比较父节点 → 更小则交换 → 循环直到根节点或父节点 ≤ 当前节点
2. 下沉操作 sink(int index)
java
private void sink(int index) {
int left = 2 * index + 1; // 左子节点索引
while (left < size) { // 存在子节点时循环
int smaller = left;
if (left + 1 < size && heap[left + 1] < heap[left]) {
smaller = left + 1; // 右子节点更小
}
if (heap[index] <= heap[smaller]) break; // 当前节点 ≤ 子节点
swap(index, smaller); // 否则交换
index = smaller; // 更新当前索引
left = 2 * index + 1; // 新的左子节点
}
}
作用:将堆顶元素从上往下调整到正确位置。
流程图:
text
堆顶元素 → 找到较小的子节点 → 如果当前节点 > 子节点 → 交换 → 循环直到无子节点或满足堆性质
完整流程模拟
假设输入 nums = [3,2,1,5,6,4], k = 2
,手动模拟堆的操作:
- 初始化堆:容量为 2
- 插入前2个元素 :
- 插入3 → 堆变为
[3]
- 插入2 → 堆变为
[2,3]
(上浮调整)
- 插入3 → 堆变为
- 处理剩余元素 :
- 遇到1:1 < 堆顶2 → 不处理
- 遇到5:5 > 2 → 移除堆顶2,插入5 → 堆变为
[3,5]
(下沉调整) - 遇到6:6 > 3 → 移除堆顶3,插入6 → 堆变为
[5,6]
- 遇到4:4 < 5 → 不处理
- 最终堆顶:5 → 返回第2大的元素是5
为什么手动实现堆重要?
- 深入理解数据结构:掌握堆的底层操作逻辑
- 应对面试要求:如果面试官禁止使用内置库,需手动实现
- 优化特殊场景:例如需要定制化堆的调整逻辑
通过理解这些核心操作,你可以灵活应对各种堆相关的变形问题!