跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C算法

C 语言实现大根堆:从原理到代码详解

基于数组存储的大根堆是优先级队列的基础。通过 C 语言完整实现了堆结构,涵盖初始化、销毁、插入、删除及上下调整等核心操作。重点解析了父子节点下标计算、动态扩容策略以及建堆时的时间复杂度优化,确保在 O(logN) 时间内维护堆性质。适合用于理解完全二叉树顺序存储特性及优先队列底层逻辑。

Pythonist发布于 2026/3/23更新于 2026/6/1421 浏览
C 语言实现大根堆:从原理到代码详解

数据结构:堆的完整实现

堆(Heap)是一种特殊的完全二叉树,通常用数组来存储。它在优先级队列、堆排序等场景中非常常见。这里我们基于 C 语言来实现一个大根堆,涵盖初始化、插入、删除以及核心的堆化操作。

堆的核心概念

普通的二叉树不适合直接用数组存储,因为会有大量空间浪费。而完全二叉树非常适合顺序存储。

注意:这里的堆是数据结构概念,与操作系统内存管理中的堆区不同。

堆的性质:

  • 父节点的值总是不大于或不小于其子节点的值。
  • 总是一棵完全二叉树。

根据大小关系分为小根堆和大根堆。本文以大根堆为例,即父节点值 >= 子节点值。

结构定义

我们用结构体来封装堆的数据,包含动态数组基地址、当前元素个数和容量。

// Heap.h
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

typedef int HeapDataType;

typedef struct HeapNode {
    HeapDataType* base; // 堆存储数组基地址
    int size;           // 当前元素个数
    int capacity;       // 堆容量
} Heap;

在数组存储的二叉树中,下标计算遵循以下规律:

  • 父节点:(child - 1) / 2
  • 左孩子:parent * 2 + 1
  • 右孩子:parent * 2 + 2

核心功能实现

1. 初始化与销毁

初始化时分配初始空间,销毁时释放资源并重置指针。

// Heap.c
void HeapInit(Heap* pheap) {
    assert(pheap);
    // 初始容量设为 4,可根据实际情况调整
    pheap->base = (HeapDataType*)malloc(sizeof(HeapDataType) * );
     (pheap->base == ) {
        perror();
        ;
    }
    pheap->size = ;
    pheap->capacity = ;
}

  {
    assert(pheap);
    (pheap->base);
    pheap->base = ;
    pheap->capacity = pheap->size = ;
}
4
if
NULL
"malloc failed\n"
return
0
4
void
HeapDestroy
(Heap* pheap)
free
NULL
0

思路解析:

  • 必须检查 malloc 是否成功,避免空指针访问。
  • 销毁后记得将指针置为 NULL,防止野指针。

2. 元素交换

堆化过程中经常需要交换父子节点的值。

void Swap(HeapDataType* child, HeapDataType* parent) {
    HeapDataType temp = *child;
    *child = *parent;
    *parent = temp;
}

3. 堆化操作

这是堆最核心的部分,分为向上调整和向下调整。

向上调整(Insert)

新元素插入到末尾后,如果破坏了堆性质,需要从下往上调整。

void AdjustUp(HeapDataType* arr, int child) {
    assert(arr);
    int parent = (child - 1) / 2;
    while (child > 0) {
        // 大根堆:子节点大于父节点则交换
        if (arr[child] > arr[parent]) {
            Swap(&arr[child], &arr[parent]);
            child = parent;
            parent = (child - 1) / 2;
        } else {
            break;
        }
    }
}

注意点:

  • 循环条件写成 child > 0 比 parent >= 0 更直观,因为当 child 为 0 时已到达堆顶。
  • 时间复杂度为 O(logN)。
向下调整(Delete/Build)

删除堆顶元素或建堆时,通常从根节点开始向下调整。

void AdjustDown(HeapDataType* arr, int size, int parent) {
    assert(arr);
    assert(parent >= 0 && parent < size);
    
    int child = parent * 2 + 1;
    while (child < size) {
        // 找出左右孩子中较大的那个
        if (child + 1 < size && arr[child + 1] > arr[child]) {
            ++child;
        }
        
        // 如果父节点小于较大子节点,则交换
        if (arr[child] > arr[parent]) {
            Swap(&arr[parent], &arr[child]);
            parent = child;
            child = parent * 2 + 1;
        } else {
            break;
        }
    }
}

关键点:

  • 总是先比较左右孩子,选出较大的一个再与父节点比较。
  • 只有当父节点小于子节点时才交换,否则说明该位置已满足堆性质。

4. 插入与删除

插入元素

直接插入末尾,然后向上调整。

void HeapPush(Heap* pheap, HeapDataType data) {
    assert(pheap);
    
    // 扩容检查:二倍扩容策略
    if (pheap->size == pheap->capacity) {
        HeapDataType* newSpace = (HeapDataType*)realloc(
            pheap->base, sizeof(HeapDataType) * pheap->capacity * 2);
        if (newSpace == NULL) {
            perror("realloc failed\n");
            return;
        }
        pheap->base = newSpace;
        pheap->capacity *= 2;
    }
    
    pheap->base[pheap->size] = data;
    pheap->size++;
    
    // 插入后向上调整
    AdjustUp(pheap->base, pheap->size - 1);
}
删除元素

删除堆顶元素,将末尾元素移到堆顶,然后向下调整。

void HeapPop(Heap* pheap) {
    assert(pheap && pheap->size > 0);
    
    // 交换堆顶和堆尾
    Swap(&pheap->base[0], &pheap->base[pheap->size - 1]);
    pheap->size--;
    
    // 非空时继续向下调整
    if (pheap->size > 0) {
        AdjustDown(pheap->base, pheap->size, 0);
    }
}

为什么这样删? 直接删除堆顶会导致后续元素前移,破坏完全二叉树结构。通过交换堆尾元素到堆顶,能最大程度保持结构完整性,只需一次向下调整即可恢复堆性质。

5. 辅助函数

bool HeapEmpty(Heap* pheap) {
    assert(pheap);
    return pheap->size == 0;
}

HeapDataType HeapTop(Heap* pheap) {
    assert(pheap);
    assert(pheap->base);
    return pheap->base[0];
}

int HeapSize(Heap* pheap) {
    assert(pheap);
    return pheap->size;
}

总结

实现堆的关键在于理解完全二叉树的数组存储特性。插入时利用向上调整维护性质,删除时利用向下调整。动态扩容保证了灵活性,断言检查确保了安全性。掌握这些逻辑后,无论是构建优先队列还是实现堆排序,都能游刃有余。

目录

  1. 数据结构:堆的完整实现
  2. 堆的核心概念
  3. 结构定义
  4. 核心功能实现
  5. 1. 初始化与销毁
  6. 2. 元素交换
  7. 3. 堆化操作
  8. 向上调整(Insert)
  9. 向下调整(Delete/Build)
  10. 4. 插入与删除
  11. 插入元素
  12. 删除元素
  13. 5. 辅助函数
  14. 总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 基于 GLM-5 与 OpenClaw 构建具备多模态能力的 AI 伴侣
  • x64 与 ARM64 架构差异及下载选择指南
  • 7 款主流 AI 论文辅助工具对比与降重技巧
  • OpenClaw 开源 AI 智能体:架构、功能与实战部署
  • 使用微 PE 启动 U 盘在 2017 MacBook Pro 上安装 Windows 单系统
  • ToDesk 顺网云海马云 DeepSeek 模型部署体验对比
  • VRM4U 插件指南:在 Unreal Engine 5 中高效处理 VRM 模型
  • ruoyi-vue-pro 数据大屏纯前端单点登录实现
  • C++ 堆数据结构原理与实现详解
  • 智源研究院开源中英双语 AltDiffusion 模型
  • 从空乘转行网络安全:零基础入门经验与面试技巧分享
  • VSCode Copilot 登录失败常见原因与解决方案
  • 学生成绩管理系统设计与实现:AI 辅助开发实战
  • Java 算法实战:随机打乱数组顺序的实现思路与代码
  • 疯狂烧钱的 AI 大模型公司如何实现商业化变现?
  • VSCode AI Copilot 智能补全失效修复指南
  • VMware 安装 CentOS 7 图文教程
  • 2025 年 12 月 GESP C++ 四级真题解析
  • 数据结构:线性表的链式表示与实现
  • 图形管线与渲染引擎中的 C++ 架构设计:模块化、跨平台与资源驱动实践

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online