手动实现一个堆

堆的基本概念

最小堆 :每个父节点的值 ≤ 子节点的值,堆顶元素是整个堆的最小值。 • 存储结构:用数组实现,索引从 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

为什么手动实现堆重要?

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

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

相关推荐
PXM的算法星球6 分钟前
【leetcode】3524 求出数组的X值1
算法·leetcode·职场和发展
椰羊~王小美3 小时前
LeetCode -- Flora -- edit 2025-04-27
算法·leetcode·职场和发展
缘友一世3 小时前
从线性回归到逻辑回归
算法·逻辑回归·线性回归
前端_学习之路4 小时前
javaScript--数据结构和算法
javascript·数据结构·算法
weixin_428498495 小时前
使用HYPRE库并行装配IJ稀疏矩阵指南: 矩阵预分配和重复利用
算法·矩阵
雾削木6 小时前
mAh 与 Wh:电量单位的深度解析
开发语言·c++·单片机·嵌入式硬件·算法·电脑
__lost6 小时前
小球在摆线上下落的物理过程MATLAB代码
开发语言·算法·matlab
mit6.8248 小时前
[Lc_week] 447 | 155 | Q1 | hash | pair {}调用
算法·leetcode·哈希算法·散列表
jerry6099 小时前
优先队列、堆笔记(算法第四版)
java·笔记·算法
勤劳的牛马10 小时前
📚 小白学算法 | 每日一题 | 算法实战:加1!
算法