前言
线性结构仅能描述一对一的顺序关系,而树与堆作为典型的非线性结构,能够高效刻画层级、从属、优先级关系,是数据结构体系中由“顺序遍历”迈向“高效筛选与调度”的关键转折。
树负责层级数据的组织与存储,依托顺序存储、链式存储两种方式适配不同形态的树形结构;堆依托完全二叉树的规整特性,基于数组实现,依靠堆序规则实现最值快速获取、数据排序与极值筛选。
本文系统梳理普通树、二叉树、完全二叉树与堆的底层逻辑,详细对比树形结构的两种存储方式,完整实现建堆、堆排序、TopK核心算法,同时归纳易混知识点、经典面试问题与实际工程应用,不涉及二叉树遍历、平衡树等复杂内容,专注夯实树与堆的核心底层能力。
一、树基础概念
1.1 树定义
树是一对多的非线性层级数据结构,节点间呈父子层级关系,无闭环、无交叉节点;区别于数组、链表一对一的线性结构,层级查找效率更高。
1.2 核心专业术语
根节点:顶层唯一无父节点的节点;
叶子节点:无任何子节点的末端节点;
父节点、子节点:直接相连的上下级节点;
节点的度:单个节点拥有子节点的数量;树的度:整棵树节点最大度;
深度/高度:树的层级总数。
1.3 树通用应用场景
操作系统文件目录、软件多级菜单、嵌入式设备层级管理、进程组织架构。
二、普通树(多叉树)两种存储结构 + 代码实现
普通树节点子节点数量不固定,存储分为链式存储(主流)、顺序存储(极少使用)。
2.1 链式存储(孩子表示法,工程通用)
采用结构体存储数据域+父节点下标+动态孩子指针数组,适配任意度的树,无空间冗余。
优缺点:增删节点灵活、无空间浪费;依靠指针寻址,无法随机访问,遍历速度慢。
2.2 顺序存储(双亲表示法,静态数组存储)
用结构体数组静态存放所有节点,额外记录父节点下标,仅适合固定节点数量的静态树,多叉树空位多,空间浪费严重。
总结:普通多叉树几乎不用顺序存储,日常开发全部采用链式存储。
三、二叉树基础 & 两种存储结构 + 完整代码
3.1 二叉树定义
每个节点最多仅存在左、右两个子节点,左右子节点严格区分顺序,是树形结构的标准基础形态。
3.2 二叉树链式存储(所有不规则二叉树唯一选择)
结构:数据域 + 左孩子指针 + 右孩子指针,适配任意形态二叉树,动态分配内存,无冗余空间。
适用场景:普通稀疏二叉树、形态杂乱的二叉树。
3.3 二叉树顺序存储(仅完全二叉树可用)
依托连续数组按层序存放节点;普通二叉树节点空缺多,数组会填充大量空标记,空间利用率极低,只有完全二叉树无空洞,适合顺序存储,也是堆的底层依托。
四、完全二叉树核心知识点(堆底层结构)
4.1 完全二叉树判定规则
节点从上至下、从左至右逐层连续填充;
中间不存在空白空位;最后一层节点全部靠左排列。
4.2 下标万能计算公式(必背)
数组下标为 i ,总节点数 n :
左孩子下标: 2*i + 1
右孩子下标: 2*i + 2
父节点下标: (i - 1) / 2 (向下取整)
最后一个非叶子节点下标: (n - 2) / 2 (建堆核心下标)
下标 (n-1)/2 之后全部为叶子节点,无需调整堆序。
五、堆精讲:定义、存储结构、全套核心代码
5.1 堆的两大硬性条件(缺一不可)
结构约束:必须是完全二叉树;
数值约束:满足堆序性;
大根堆:父节点值 ≥ 左右孩子值,堆顶为全局最大值;
小根堆:父节点值 ≤ 左右孩子值,堆顶为全局最小值。
5.2 堆存储结构说明
堆只采用顺序存储(一维数组),完全不使用链式存储:依靠下标公式直接计算父子节点,无指针开销,下沉调整、排序效率极高;链式结构无法通过数学索引定位子节点,堆操作效率极低。
5.3 堆核心:下沉调整函数(所有堆操作共用)
下沉是堆最底层逻辑,建堆、堆排序、TopK全部复用此代码,仅调用起始下标不同。以大根堆为例:
5.4 建堆函数代码
从最后一个非叶子节点倒序向前遍历,逐个执行下沉调整,时间复杂度O(n)
5.5 堆排序完整实现代码
思路:建大根堆 → 堆顶最值与数组末尾交换 → 根节点下沉修复堆 → 循环遍历完成升序排序;稳定时间复杂度O(nlogn),属于不稳定排序。
5.6 面试高频:TopK问题代码实现
需求:从海量数组中筛选前K大元素,最优方案为构建长度K的小根堆,时间复杂度O(nlogK),海量数据优势极大。
六、树&二叉树&堆 存储结构横向对比(背诵表格)
表格
七、核心易错点 & 高频面试问答
面试问答1:堆为什么只使用数组顺序存储,不用链式存储?
堆依赖完全二叉树下标公式快速定位父子节点,下沉调整、堆排序需要频繁访问子节点;链式依靠指针遍历查找,效率极低,且完全二叉树数组无空间浪费,计算开销远小于指针寻址。
面试问答2:普通二叉树不能用顺序存储的原因?
形态不规则的二叉树存在大量空节点,顺序存储数组需要填充占位标记,产生严重空间浪费,空间利用率极低。
面试问答3:建堆与堆排序下沉起点不一致的原因?
建堆:原始数组完全无序,整棵树结构全部混乱,需要从最底层非叶子节点从下往上调整,保证下层子树先合法;
堆排序:仅堆顶节点交换后破坏堆序,下层子树依旧是合法堆,只需要从根节点0单独下沉修复即可。
面试问答4:堆排序是否为稳定排序?
不稳定;相等数值元素在交换过程中会改变原始相对先后顺序。
面试问答5:TopK筛选前K大元素为什么构建小根堆而非大根堆?
小根堆堆顶是当前K个数据里的最小值,作为筛选门槛,新元素大于门槛值则替换堆顶,小于直接舍弃,筛选逻辑简单,时间复杂度最优。
八、工程落地应用场景
嵌入式(STM32/FreeRTOS/UCOS)
RTOS操作系统任务优先级队列底层基于大根堆实现;
ADC传感器采集数据剔除极值、均值滤波,使用TopK堆筛选;
设备日志、采集数据批量排序采用堆排序;
软件多级菜单使用普通树链式存储搭建层级结构。
后端软件开发
定时任务调度、优先级消息队列;
榜单热度排行、海量日志极值筛选;
大数据流式数据TopK实时过滤。
九、全文核心总结
树是非线性层级结构,普通多叉树、不规则二叉树采用链式存储;完全二叉树、堆固定使用数组顺序存储;
堆本质是带堆序约束的完全二叉树,下沉函数是堆全部操作的核心逻辑;
建堆复杂度O(n),堆排序、TopK复杂度O(nlogn),海量数据处理优势明显;
存储结构选择核心依据:树形节点是否紧凑连续,连续选顺序存储,稀疏杂乱选用链式存储;
本文规避二叉树遍历、平衡树等超纲内容,聚焦基础原理、存储结构、可运行代码、面试考点,适配期末、校招、嵌入式学习全部需求。
结语
本篇博客整合了树与堆的概念、两种存储结构定义+完整C语言代码、堆全套算法实现、面试高频问答、工程实战场景,内容精简不冗余,无多余超纲知识点,直接复制即可发布到CSDN。后续持续更新数据结构手撕代码、嵌入式底层原理、算法面试真题,欢迎点赞收藏关注博主。