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

数据结构:二叉树进阶——堆的实现与原理

本文深入讲解数据结构中的堆,涵盖大根堆与小根堆定义、完全二叉树数组存储特性及下标计算规则。重点剖析入堆时的向上调整与出堆时的向下调整算法逻辑,提供完整的 C 语言实现代码,包括初始化、扩容、插入、删除及销毁操作。内容旨在帮助开发者理解堆的高效性与底层机制,适用于面试准备及算法实战。

内存管理发布于 2026/3/16更新于 2026/6/1223 浏览
数据结构:二叉树进阶——堆的实现与原理

一、二叉树基础回顾

在树形结构中,二叉树是最常用的形态之一。顾名思义,二叉树的每个节点最多有两个子节点,分别称为左子树和右子树。一颗二叉树由一个根节点加上两棵互不相交的二叉树组成,或者为空。

二叉树具有以下核心特点:

  1. 不存在度大于 2 的结点。
  2. 左右子树有严格顺序之分,次序不可颠倒,因此二叉树是有序树。

无论结构如何变化,二叉树的基本形态都由以下几种情况复合而成:空树、单根节点、左/右子树非空等组合。

二、特殊二叉树类型

2.1 满二叉树

满二叉树是指除了叶子结点外,其余每个节点都有两个子树。简单来说,每一层的结点数都达到了最大值。若深度为 k,则第 i 层有 2^(i-1) 个结点,总结点数为 2^k - 1。

反之,若一棵深度为 k 的二叉树拥有 2^k - 1 个结点,它必然是满二叉树。

2.2 完全二叉树

完全二叉树是一种效率很高的结构,通常由满二叉树引申而来。若深度为 k、有 n 个结点的二叉树,其每一个结点都与深度为 k 的满二叉树中编号从 1 到 n 的结点一一对应,则称之为完全二叉树。

注意: 满二叉树是完全二叉树的特例。在完全二叉树中,最后一层不一定填满,但除最后一层外,其他层必须满。

相关性质:

  • 若规定根结点层数为 1,则第 i 层最多有 2^(i-1) 个结点。
  • 深度为 h 的二叉树最大结点数为 2^h - 1。
  • 具有 n 个结点的满二叉树深度 h = log₂(n+1)。

2.3 其他类型

除上述两种外,常见的还有平衡二叉树、二叉搜索树、红黑树、哈夫曼树等,它们在不同场景下各有优劣。

三、二叉树的存储结构

线性表有顺序和链式存储,二叉树亦然。

3.1 顺序存储

顺序存储利用数组存放数据。这种方式特别适合表示完全二叉树,因为可以精确计算每个元素的位置,避免空间浪费。

对于完全二叉树,根节点位于数组索引 0(或 1),随后按层级从左到右依次排列。若非完全二叉树强行使用顺序存储,中间的空缺位置会占用大量内存,导致效率低下。

提示: 实际开发中,堆(Heap)常采用顺序结构的数组来存储。需注意这里的'堆'与操作系统内存管理中的'堆区'概念不同,前者是数据结构,后者是内存区域。

3.2 链式存储

链式存储通过链表节点表示二叉树。每个节点包含数据域和左右指针域,分别指向左右孩子。这种结构灵活性高,适合非完全二叉树。

此外还有三叉链表,增加了父节点指针,常用于红黑树等高级结构,但在 C 语言基础教学中,二叉链表更为常见。

四、堆的概念与实现

堆是一种特殊的完全二叉树,满足特定父子节点的大小关系。它是顺序结构二叉树的典型应用。

4.1 堆的定义

若关键码集合 K = {k₀, k₁, ..., kₙ₋₁} 按完全二叉树顺序存储在一维数组中,且满足:

  • 小根堆:Kᵢ ≤ K₂ᵢ₊₁ 且 Kᵢ ≤ K₂ᵢ₊₂
  • 大根堆:Kᵢ ≥ K₂ᵢ₊₁ 且 Kᵢ ≥ K₂ᵢ₊₂

则称该结构为小根堆或大根堆。通俗讲,大根堆的父节点值不小于子节点,小根堆则相反。

4.2 数组下标性质

对于含 n 个结点的完全二叉树,若按层序从 0 开始编号,编号为 i 的节点满足:

  • 父节点索引:(i - 1) / 2 (当 i > 0)
  • 左孩子索引:2 * i + 1 (若 < n)
  • 右孩子索引:2 * i + 2 (若 < n)

这一性质使得我们在数组中无需额外指针即可定位父子关系。

4.3 堆的核心操作

我们围绕增、删、查三个方面来实现堆。

4.3.1 结构与初始化

堆的底层是动态数组,结构体设计类似顺序表:







  Elemtype;

 
    Elemtype* arr;
     size;   
     length; 
} Heap;
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef
int
typedef
struct Heap {
int
// 当前有效元素个数
int
// 当前总容量

初始化时分配 NULL 并置零:

void Init_Heap(Heap* pHeap) {
    pHeap->arr = NULL;
    pHeap->size = pHeap->length = 0;
}
4.3.2 入堆(插入)

插入操作需遵循完全二叉树的特性,新元素只能追加到数组末尾。插入后可能破坏堆序性,因此需要向上调整。

向上调整算法思路: 将新节点与其父节点比较,若不符合堆序(如大根堆中子节点大于父节点),则交换位置,继续向上直至满足条件。

// 向上调整(大顶堆)
void adjustUP(Elemtype* arr, int child) {
    int parent = (child - 1) / 2;
    while (child > 0) {
        if (*(arr + parent) < *(arr + child)) {
            Swap(arr + parent, arr + child);
            child = parent;
            parent = (child - 1) / 2;
        } else {
            break;
        }
    }
}

// 堆插入
void Push_Heap(Heap* pHeap, Elemtype data) {
    // 扩容逻辑
    if (pHeap->size == pHeap->length) {
        int newLength = pHeap->length == 0 ? 4 : 2 * pHeap->length;
        Elemtype* newbase = (Elemtype*)realloc(pHeap->arr, newLength * sizeof(Elemtype));
        if (!newbase) {
            perror("内存申请失败");
            return;
        }
        pHeap->arr = newbase;
        pHeap->length = newLength;
    }
    // 插入并调整
    pHeap->arr[pHeap->size] = data;
    adjustUP(pHeap->arr, pHeap->size);
    pHeap->size++;
}
4.3.3 出堆(删除)

堆的删除通常针对堆顶元素(最值)。直接删除会破坏结构,标准做法是将堆尾元素移至堆顶,再向下调整。

向下调整算法思路: 比较父节点与左右子节点,若父节点小于较大子节点(大根堆),则交换,并继续向下,直到满足堆序。

// 交换函数
void Swap(Elemtype* a, Elemtype* b) {
    Elemtype temp = *a;
    *a = *b;
    *b = temp;
}

// 向下调整(大顶堆)
void adjustDown(Elemtype* arr, int size, int parent) {
    assert(arr);
    int child = parent * 2 + 1;
    while (child < size) {
        // 找出较大的子节点
        if (child + 1 < size && *(arr + child) < *(arr + child + 1)) {
            child++;
        }
        // 父节点与较大子节点比较
        if (*(arr + parent) < *(arr + child)) {
            Swap(arr + parent, arr + child);
            parent = child;
            child = parent * 2 + 1;
        } else {
            break;
        }
    }
}

// 堆删除
void Pop_Heap(Heap* pHeap) {
    assert(!isEmpty(pHeap));
    // 堆顶与堆尾交换
    Swap(pHeap->arr, pHeap->arr + pHeap->size - 1);
    pHeap->size--;
    // 向下调整恢复堆序
    adjustDown(pHeap->arr, pHeap->size, 0);
}
4.3.4 辅助操作

判空只需检查 size 是否为 0;取最值直接返回数组首元素;销毁需释放内存并重置状态。

bool isEmpty(Heap* pHeap) {
    assert(pHeap);
    return pHeap->size == 0;
}

Elemtype Top_Heap(Heap* pHeap) {
    return pHeap->arr[0];
}

void Destory_Heap(Heap* pHeap) {
    free(pHeap->arr);
    pHeap->arr = NULL;
    pHeap->size = pHeap->length = 0;
}

4.4 完整示例代码

以下是一个完整的堆实现框架,支持大小顶堆切换:

// 小顶堆调整略作修改即可,此处省略重复代码
// 主函数演示
int main() {
    Heap Hp;
    Heap* pHeap = &Hp;
    Init_Heap(pHeap);

    // 模拟插入
    for (int i = 0; i < 5; i++) {
        Push_Heap(pHeap, i * 10);
    }

    // 查看堆顶
    printf("Top: %d\n", Top_Heap(pHeap));

    // 删除堆顶
    Pop_Heap(pHeap);
    printf("New Top: %d\n", Top_Heap(pHeap));

    Destory_Heap(pHeap);
    return 0;
}

五、总结

堆作为完全二叉树的顺序存储实现,利用数组下标规律高效维护父子关系。通过向上调整和向下调整算法,我们可以在 O(log n) 时间内完成插入和删除操作。掌握堆的原理,不仅有助于理解优先队列,也是学习排序算法(如堆排序)的基础。

目录

  1. 一、二叉树基础回顾
  2. 二、特殊二叉树类型
  3. 2.1 满二叉树
  4. 2.2 完全二叉树
  5. 2.3 其他类型
  6. 三、二叉树的存储结构
  7. 3.1 顺序存储
  8. 3.2 链式存储
  9. 四、堆的概念与实现
  10. 4.1 堆的定义
  11. 4.2 数组下标性质
  12. 4.3 堆的核心操作
  13. 4.3.1 结构与初始化
  14. 4.3.2 入堆(插入)
  15. 4.3.3 出堆(删除)
  16. 4.3.4 辅助操作
  17. 4.4 完整示例代码
  18. 五、总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • RK3588 部署自训练 YOLO11 模型实战:ONNX 转 RKNN 及 C++ 推理
  • Python in Excel 功能特性与使用心得
  • 深入解析 CAN 通信:接收、发送与中断处理
  • 可信纵深防御建设方案:应用可信与网络可信
  • Mission Planner 无人机地面站软件操作指南
  • Java 应用程序被安全阻止:原因分析与解决方案
  • 网络安全行业自学与转行学习路径建议
  • 前端科技新闻(WTN-4)你用了免费的 Trae 编辑器吗?排队多少名?我排在1584名
  • MixAIHub 提供 ChatGPT Claude Sora 等 AI 官网镜像服务
  • AI 学术写作与查重工具功能解析
  • 前缀和算法详解与应用
  • C++ unordered 系列容器认识与模拟实现
  • AI 大模型技术前沿与产业应用双轮驱动
  • 基于 Three.js 渲染三维无人机模型(WebGL / Vue / React)
  • 前端请求后端 404/405/500 状态码排查与解决实战
  • Terraform 基础设施即代码入门与实践
  • Python 字节数组(bytes)的创建与格式化输出
  • GFPGAN 跨平台部署与人脸修复实战指南
  • AI 大模型通信机制:流式传输与数据封装逻辑
  • Llama 开源家族梳理:从 Llama-1 到 Llama-3 演进解析

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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