手动实现一个堆

堆的基本概念

最小堆 :每个父节点的值 ≤ 子节点的值,堆顶元素是整个堆的最小值。 • 存储结构:用数组实现,索引从 0 开始,父子节点关系:

  • 父节点索引 = (子节点索引 - 1) / 2
  • 左子节点索引 = 父节点索引 * 2 + 1
  • 右子节点索引 = 父节点索引 * 2 + 2

代码结构总览

手动堆的实现围绕以下四个核心方法:

  1. insert(int num):插入元素并调整堆结构
  2. removeMin():移除堆顶元素并调整堆结构
  3. swim(int index):上浮操作,用于插入元素时调整
  4. sink(int index):下沉操作,用于移除元素时调整

分步详解

步骤 1:初始化堆

java 复制代码
heap = new int[k];  // 创建一个大小为k的数组
size = 0;           // 当前堆中元素个数

步骤 2:插入元素 (insert 方法)

目标:将新元素插入堆中,并维持最小堆性质。

操作流程

  1. 将元素放到数组末尾heap[size] = num;
  2. 执行上浮 (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 方法)

目标:移除堆顶(最小值)后,将最后一个元素移到堆顶,并下沉调整。

操作流程

  1. 将堆顶与最后一个元素交换heap[0] = heap[size-1];
  2. 执行下沉 (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,手动模拟堆的操作:

  1. 初始化堆:容量为 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 → 不处理
  4. 最终堆顶:5 → 返回第2大的元素是5

为什么手动实现堆重要?

  • 深入理解数据结构:掌握堆的底层操作逻辑
  • 应对面试要求:如果面试官禁止使用内置库,需手动实现
  • 优化特殊场景:例如需要定制化堆的调整逻辑

通过理解这些核心操作,你可以灵活应对各种堆相关的变形问题!

相关推荐
张彦峰ZYF23 分钟前
高频面试题(含笔试高频算法整理)基本总结回顾63
linux·运维·算法
alphaTao1 小时前
LeetCode 每日一题 2025/3/31-2025/4/6
算法·leetcode
Andrew_Ryan1 小时前
android use adb instsll cacerts
算法·架构
Wx120不知道取啥名2 小时前
C语言跳表(Skip List)算法:数据世界的“时光穿梭机”
c语言·数据结构·算法·list·跳表算法
禾小西3 小时前
Java 逐梦力扣之旅_[204. 计数质数]
java·算法·leetcode
LuckyLay3 小时前
LeetCode算法题(Go语言实现)_32
算法·leetcode·golang
ゞ 正在缓冲99%…3 小时前
leetcode295.数据流的中位数
java·数据结构·算法·leetcode·
文弱_书生3 小时前
关于点扩散函数小记
数码相机·算法·数学原理
爪娃侠3 小时前
LeetCode热题100记录-【二叉树】
linux·算法·leetcode
圣保罗的大教堂4 小时前
《算法笔记》9.8小节——图算法专题->哈夫曼树 问题 E: 合并果子-NOIP2004TGT2
算法