数据结构:二叉树

二叉树从入门到实战:C语言实现与核心应用指南

一、为什么要学二叉树?

作为数据结构体系里承上启下的核心模块,二叉树是你从线性表跨入非线性结构的第一道门槛。它不仅是后续学习堆、平衡树、红黑树的基础,更是大量工程场景的底层支撑:

  • 二叉搜索树可以实现O(logN)级别的高效查找

  • 算术表达式可以直接构建为二叉树,前序遍历得到前缀表达式、后序遍历得到后缀表达式

  • 智能电网仿真中的层级化故障索引,用二叉树存储后查询效率能提升数倍

  • 操作系统的文件目录、游戏中的技能树,本质都是二叉树的衍生应用

二、二叉树核心基础概念

2.1 基本定义

二叉树是每个节点最多拥有左右两个子节点的有序树形结构,子树严格区分左右顺序,不能随意颠倒。它是递归定义的:一棵非空二叉树由根节点、左子树、右子树组成,而左右子树本身也都是二叉树。

2.2 两类高频特殊二叉树

  1. **满二叉树**:每一层的节点数都达到最大值,深度为k的满二叉树总节点数为`2^k - 1`,没有任何空位。

  2. **完全二叉树**:从上到下、从左到右依次填充节点,除了最底层外其余所有层都被填满,最底层的节点全部集中在左侧。满二叉树是特殊的完全二叉树,我们日常用的堆结构就是完全二叉树的典型实现。

2.3 必记核心性质

  • 第i层的最大节点数为`2^(i-1)`

  • 深度为h的二叉树最多有`2^h - 1`个节点

  • 对于n个节点的完全二叉树,按数组从0开始编号时,下标为i的节点左孩子下标为`2*i+1`,右孩子下标为`2*i+2`,父节点下标为`(i-1)/2`

三、二叉树的两种存储方式

3.1 顺序存储

直接用数组存储二叉树节点,仅适合完全二叉树,非完全二叉树会产生大量空间浪费。我们之前学的堆排序、优先级队列都是基于这种存储方式实现的。

3.2 链式存储

最通用的存储方案,每个节点包含数据域、左孩子指针、右孩子指针,C语言结构体定义如下:

`typedef int BTDataType;`\

`typedef struct BinaryTreeNode`\

`{`\

` BTDataType _val;`\

` struct BinaryTreeNode* _left;`\

` struct BinaryTreeNode* _right;`\

`}BTNode;`

这种方式不会产生空间浪费,是我们后续实现所有二叉树操作的基础。

四、二叉树四大遍历方式实现

遍历是二叉树所有操作的基础,本质是按特定规则访问树中所有节点,分为深度优先和广度优先两大类:

4.1 前序遍历(根→左→右)

先访问根节点,再递归遍历左子树,最后递归遍历右子树:

`void PrevOrder(BTNode* root)`\

`{`\

` if(root == NULL)`\

` {`\

` printf("N ");`\

` return;`\

` }`\

` printf("%d ", root->_val);`\

` PrevOrder(root->_left);`\

` PrevOrder(root->_right);`\

`}`

4.2 中序遍历(左→根→右)

先递归遍历左子树,再访问根节点,最后递归遍历右子树。对于二叉搜索树,中序遍历可以直接得到升序的有序序列:

`void InOrder(BTNode* root)`\

`{`\

` if(root == NULL)`\

` {`\

` printf("N ");`\

` return;`\

` }`\

` InOrder(root->_left);`\

` printf("%d ", root->_val);`\

` InOrder(root->_right);`\

`}`

4.3 后序遍历(左→右→根)

先递归遍历左子树,再递归遍历右子树,最后访问根节点。二叉树的销毁操作就非常适合用后序遍历实现,先释放左右子节点再释放根节点,避免野指针:

`void PostOrder(BTNode* root)`\

`{`\

` if(root == NULL)`\

` {`\

` printf("N ");`\

` return;`\

` }`\

` PostOrder(root->_left);`\

` PostOrder(root->_right);`\

` printf("%d ", root->_val);`\

`}`

4.4 层序遍历

借助队列实现广度优先遍历,从上到下、从左到右按层级访问所有节点,是求解二叉树宽度、判断完全二叉树的核心方法。

五、高频基础接口实现

5.1 计算二叉树节点总数

利用后序遍历的递归思路,左子树节点数 + 右子树节点数 + 1(当前根节点):

`int BinaryTreeSize(BTNode* root)`\

`{`\

` if(root == NULL)`\

` return 0;`\

` return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;`\

`}`

5.2 统计叶子节点个数

叶子节点的判定条件是左右孩子都为空,递归统计左右子树的叶子节点之和即可:

`int BinaryTreeLeafSize(BTNode* root)`\

`{`\

` if(root == NULL)`\

` return 0;`\

` if(root->_left == NULL && root->_right == NULL)`\

` return 1;`\

` return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);`\

`}`

5.3 求二叉树的高度

递归取左右子树的最大高度,加1就是当前树的高度:

`int BinaryTreeHeight(BTNode* root)`\

`{`\

` if(root == NULL)`\

` return 0;`\

` int leftHeight = BinaryTreeHeight(root->_left);`\

` int rightHeight = BinaryTreeHeight(root->_right);`\

` return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;`\

`}`

六、经典算法:最近公共祖先

给定二叉树的两个节点p和q,找到它们的最近公共祖先,核心逻辑用递归实现非常简洁:

  1. 如果当前节点为空,直接返回NULL

  2. 如果当前节点就是p或者q,直接返回当前节点

  3. 递归遍历左子树和右子树,如果左右子树返回值都不为空,说明p和q分别在左右子树中,当前节点就是最近公共祖先

  4. 如果只有一侧返回非空值,说明两个节点都在该侧子树中,直接返回该侧的递归结果

七、工程落地小提示

结合你正在做的电力系统仿真场景,你可以把电网的层级化故障索引存储为二叉树,利用中序遍历快速检索不同电压等级的故障记录,比线性遍历效率提升非常明显。同时在Linux环境下用GCC编译调试这些代码,也能和你之前学的Makefile多文件管理知识形成完整的技术闭环。

相关推荐
iNeuOS工业互联网1 小时前
iNeuOS_Vision_视觉分析,增加SAM3模型对实例分割和目标检测AI自动标注图片样本功能
人工智能·算法·目标检测
qeen871 小时前
【数据结构】红黑树的算法原理解析与实现
开发语言·数据结构·c++·算法·红黑树
码匠许师傅2 小时前
【C++ 面试真题】20. 聊聊 C++ 的标准库常用算法
c++·算法·面试
m0_547486662 小时前
《数据结构与算法(Python语言版)》全套PPT课件2026
数据结构·算法
个 人 练 习 生3 小时前
数据结构入门:算法复杂度
开发语言·数据结构·经验分享·学习·程序人生·算法
Ricardo-Yang3 小时前
无人机单目深度估计测试:ZipDepth 与 AerialMetric
人工智能·算法·机器学习·计算机视觉·无人机
Nil2083 小时前
golang解决单词转换
算法
imaol14 小时前
文件编程--标准IO
linux·运维·算法
青 春 记 忆4 小时前
LeetCode 121. 买卖股票的最佳时机|Python 解法详解
python·算法·leetcode