Cyber骇客的逻辑节点美学 ——【初阶数据结构与算法】二叉树

⚡ CYBER_PROFILE ⚡
/// SYSTEM READY ///


WARNING \]: DETECTING HIGH ENERGY **🌊 🌉 🌊 心手合一 · 水到渠成** ![分隔符](https://i-blog.csdnimg.cn/direct/60a3de2294e9439abad47378e657b337.gif) |------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------| | **\>\>\> ACCESS TERMINAL \<\<\<** || | [**\[ 🦾 作者主页 \]**](https://blog.csdn.net/fengtinghuqu520?spm=1000.2115.3001.5343) | [**\[ 🔥 C语言核心 \]**](https://blog.csdn.net/fengtinghuqu520/category_12955956.html) | | [**\[ 💾 编程百度 \]**](https://blog.csdn.net/fengtinghuqu520/category_13083835.html) | [**\[ 📡 代码仓库 \]**](https://blog.csdn.net/fengtinghuqu520/article/details/147275999?spm=1001.2014.3001.5502) | --------------------------------------- Running Process: 100% \| Latency: 0ms *** ** * ** *** 🚩在之前的`树`的章节中我们讲解了**树这个数据结构的一些基本概念** ,本章我们将重点介绍树结构中最常见的**二叉树** > 🔗[Lucy的空间骇客裂缝:树](https://blog.csdn.net/fengtinghuqu520/article/details/156150995?spm=1011.2415.3001.10575&sharefrom=mp_manage_link) #### 索引与导读 * [🤔一、什么是二叉树?](#🤔一、什么是二叉树?) * [🤔二、二叉树的情况分布](#🤔二、二叉树的情况分布) * [🤔三、特殊二叉树](#🤔三、特殊二叉树) * * [满二叉树](#满二叉树) * [完全二叉树](#完全二叉树) * [🤔四、二叉树的性质](#🤔四、二叉树的性质) * * [二叉树的普通性质(所有二叉树适用)](#二叉树的普通性质(所有二叉树适用)) * * [性质 1:第 i i i 层上的结点数](#性质 1:第 i i i 层上的结点数) * [证明(数学归纳法):](#证明(数学归纳法):) * [性质 2:深度为 k k k 的最大结点总数](#性质 2:深度为 k k k 的最大结点总数) * [性质 3:叶子结点与度为2结点的关系 【考研/面试高频】](#性质 3:叶子结点与度为2结点的关系 【考研/面试高频】) * [完全二叉树的特有性质](#完全二叉树的特有性质) * * [性质 4:完全二叉树的深度](#性质 4:完全二叉树的深度) * [性质5:完全二叉树的数组存储特性](#性质5:完全二叉树的数组存储特性) * [经典例题](#经典例题) * * [1. 叶子结点与度为2结点的关系](#1. 叶子结点与度为2结点的关系) * [2. 顺序存储结构的适用性](#2. 顺序存储结构的适用性) * [3. 完全二叉树的叶子结点推导](#3. 完全二叉树的叶子结点推导) * [4. 完全二叉树的高度计算](#4. 完全二叉树的高度计算) * [5. 大规模完全二叉树的叶子计算](#5. 大规模完全二叉树的叶子计算) * [总结技巧](#总结技巧) * [🤔五、二叉树的存储结构](#🤔五、二叉树的存储结构) * * * [5.1)顺序存储](#5.1)顺序存储) * [5.2)链式存储](#5.2)链式存储) * * [5.2.1)二叉树的遍历方式](#5.2.1)二叉树的遍历方式) * * [1)前序遍历](#1)前序遍历) * [2)中序遍历](#2)中序遍历) * [3)后序遍历](#3)后序遍历) * [4)层序遍历](#4)层序遍历) * [5.2.2)🔨二叉树遍历的经典例题](#5.2.2)🔨二叉树遍历的经典例题) * * * [🎯 第一题:完全二叉树的序列转化](#🎯 第一题:完全二叉树的序列转化) * [🎯 第二题:秒杀根节点(送分题)](#🎯 第二题:秒杀根节点(送分题)) * [🎯 第三题:由中序与后序还原前序(经典难点)](#🎯 第三题:由中序与后序还原前序(经典难点)) * [🤔六、二叉树的分文件编写](#🤔六、二叉树的分文件编写) * * [BinaryTree.h](#BinaryTree.h) * [BinaryTree.c](#BinaryTree.c) * * [简易队列的创建(用于 LevelOrder 和 BinaryTreeComplete)](#简易队列的创建(用于 LevelOrder 和 BinaryTreeComplete)) * * [1)结构体的创建](#1)结构体的创建) * [2)初始化队列](#2)初始化队列) * [3)入队](#3)入队) * [4)出队](#4)出队) * [5)获取队头元素](#5)获取队头元素) * [6)判断队列是否为空](#6)判断队列是否为空) * [7)销毁队列](#7)销毁队列) * [🌠创建新节点](#🌠创建新节点) * [🌠通过前序数组构建二叉树](#🌠通过前序数组构建二叉树) * [🌠二叉树销毁 (后序遍历销毁)](#🌠二叉树销毁 (后序遍历销毁)) * [🌠节点个数](#🌠节点个数) * [🌠叶子节点个数(递归实现)](#🌠叶子节点个数(递归实现)) * [🌠第k层节点个数](#🌠第k层节点个数) * [🌠查找值为x的节点](#🌠查找值为x的节点) * [🌠前序遍历: 根 -\> 左 -\> 右](#🌠前序遍历: 根 -> 左 -> 右) * [🌠中序遍历: 左 -\> 根 -\> 右](#🌠中序遍历: 左 -> 根 -> 右) * [🌠后序遍历: 左 -\> 右 -\> 根](#🌠后序遍历: 左 -> 右 -> 根) * [🌠层序遍历 (使用队列)](#🌠层序遍历 (使用队列)) * [🌠判断是否为完全二叉树](#🌠判断是否为完全二叉树) * [🌠二叉树的高度](#🌠二叉树的高度) * [🖊完整代码](#🖊完整代码) * [test.c](#test.c) * [🤔关于二叉树的在线OJ题](#🤔关于二叉树的在线OJ题) * [💻结尾--- 核心连接协议](#💻结尾— 核心连接协议) ## 🤔一、什么是二叉树? 🚩二叉树(Binary Tree)是 n n n 个节点的有限集合。在 C 语言中,我们通常通过**结构体(struct)**和**指针**来模拟这种非线性的逻辑结构 * 一棵二叉树是结点的一个有限集合,该集合: 1. 或者为空 2. 由一个根结点加上两棵别称为左子树和右子树的二叉树组成 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/d17f36c8f23743eba7b2cb59d4130859.png) **从上图我们可以看出:** 🚩二叉树不存在度大于2的结点 🚩二叉树的子树有左右之分,次序不能颠倒 > **因此二叉树是有序树** *** ** * ** *** ## 🤔二、二叉树的情况分布 🚩对于任意二叉树,**都是由以下几种情况复合而成的** :![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/66cae8bcccef49ae963da2cc313dfb0f.png) *** ** * ** *** ## 🤔三、特殊二叉树 在普通二叉树中,节点的位置比较随意 但在**满二叉树** 和**完全二叉树** 中,节点的分布遵循严格的规律 这种规律使得我们可以**用数组来高效存储二叉树** ,而不需要总是依赖**链表** ### 满二叉树 **定义**:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树 * **核心公式** 假设树的高度为 h h h(从1开始计数): * 第 i i i 层的节点数: 2 i − 1 2\^{i-1} 2i−1 * 总节点数 N N N: 2 h − 1 2\^h - 1 2h−1 * **💡 面试考点:** **如果已知节点总数 N N N 是偶数,它一定不是满二叉树** * **图解示例** ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/3e32d1925d754ab491d4d3aebc9fb839.png) ### 完全二叉树 * **定义:** 完全二叉树是效率很高的数据结构,对于深度为 h h h 的二叉树,前 h − 1 h-1 h−1 层必须是满的 第 h h h 层(最后一层)的节点必须从左到右连续排列,中间不能留空 > 满二叉树一定是完全二叉树,但反之不成立 > 完全二叉树允许最后一层没填满,但必须紧凑地靠在左边 * **图解示例** ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/765ee1a82b514e259ab16d3426e0738d.png) *** ** * ** *** ## 🤔四、二叉树的性质 ### 二叉树的普通性质(所有二叉树适用) 这是二叉树通用的三大性质,无论是满二叉树、完全二叉树还是普通二叉树,都必须遵循。 #### 性质 1:第 i i i 层上的结点数 > **结论** :二叉树的第 i i i 层上至多有 2 i − 1 2\^{i-1} 2i−1 个结点 ( i ≥ 1 i \\ge 1 i≥1)。 #### 证明(数学归纳法): 1. **基础步骤** :当 i = 1 i=1 i=1 时,第1层只有根结点,结点数为 1 = 2 1 − 1 = 2 0 1 = 2\^{1-1} = 2\^0 1=21−1=20,命题成立。 2. **归纳假设** :假设第 i − 1 i-1 i−1 层至多有 2 i − 2 2\^{i-2} 2i−2 个结点。 3. **推导** :根据二叉树定义,每个结点最多有两个孩子。因此,第 i i i 层的结点数最多是第 i − 1 i-1 i−1 层结点数的 2 倍。 M a x ( N i ) = 2 × M a x ( N i − 1 ) = 2 × 2 i − 2 = 2 i − 1 Max(N_i) = 2 \\times Max(N_{i-1}) = 2 \\times 2\^{i-2} = 2\^{i-1} Max(Ni)=2×Max(Ni−1)=2×2i−2=2i−1 **证毕。** *** ** * ** *** #### 性质 2:深度为 k k k 的最大结点总数 > **结论** :深度为 k k k 的二叉树至多有 2 k − 1 2\^k - 1 2k−1 个结点 ( k ≥ 1 k \\ge 1 k≥1)。 * **证明(等比数列求和):** 深度为 k k k 的二叉树,要想结点最多,那么每一层都必须达到最大值(即满二叉树)。 根据性质1,各层最大结点数分别为: 2 0 , 2 1 , 2 2 , . . . , 2 k − 1 2\^0, 2\^1, 2\^2, ..., 2\^{k-1} 20,21,22,...,2k−1。 总数 S k S_k Sk 为: S k = ∑ i = 1 k 2 i − 1 = 2 0 + 2 1 + ⋯ + 2 k − 1 S_k = \\sum_{i=1}\^{k} 2\^{i-1} = 2\^0 + 2\^1 + \\dots + 2\^{k-1} Sk=i=1∑k2i−1=20+21+⋯+2k−1 根据等比数列求和公式: S k = a 1 ( 1 − q k ) 1 − q = 1 ( 1 − 2 k ) 1 − 2 = 2 k − 1 S_k = \\frac{a_1(1-q\^k)}{1-q} = \\frac{1(1-2\^k)}{1-2} = 2\^k - 1 Sk=1−qa1(1−qk)=1−21(1−2k)=2k−1 **证毕。** *** ** * ** *** #### 性质 3:叶子结点与度为2结点的关系 【考研/面试高频】 > **结论** :对任何一棵二叉树 T T T,如果其叶子结点(度为0)数为 n 0 n_0 n0,度为2的结点数为 n 2 n_2 n2,则 n 0 = n 2 + 1 n_0 = n_2 + 1 n0=n2+1。 * **详细推导:** 这是一个非常精妙的推导,利用了"结点数"和"分支数(边数)"的两个等式。 **设:** * n n n 为二叉树结点总数。 * n 0 n_0 n0 为度为0的结点数(叶子)。 * n 1 n_1 n1 为度为1的结点数。 * n 2 n_2 n2 为度为2的结点数。 * B B B 为分支总数(即树枝/边的数量)。 **步骤 1:从结点组成看总数** 二叉树只有三种类型的结点(度为0, 1, 2),所以: n = n 0 + n 1 + n 2 ------ (式1) n = n_0 + n_1 + n_2 \\quad \\text{------ (式1)} n=n0+n1+n2------ (式1) **步骤 2:从分支(边)看总数** * 除了根结点外,每个结点都有且仅有一条边从上面连下来。 * 所以,结点总数 = 分支总数 + 1(根结点)。 n = B + 1 ------ (式2) n = B + 1 \\quad \\text{------ (式2)} n=B+1------ (式2) **步骤 3:计算分支总数 B B B** * 度为0的结点发出 0 条边。 * 度为1的结点发出 1 条边。 * 度为2的结点发出 2 条边。 B = 0 × n 0 + 1 × n 1 + 2 × n 2 = n 1 + 2 n 2 B = 0 \\times n_0 + 1 \\times n_1 + 2 \\times n_2 = n_1 + 2n_2 B=0×n0+1×n1+2×n2=n1+2n2 **步骤 4:联立求解** 将 B B B 代入 (式2): n = n 1 + 2 n 2 + 1 ------ (式3) n = n_1 + 2n_2 + 1 \\quad \\text{------ (式3)} n=n1+2n2+1------ (式3) 联立 (式1) 和 (式3): n 0 + n 1 + n 2 = n 1 + 2 n 2 + 1 n_0 + n_1 + n_2 = n_1 + 2n_2 + 1 n0+n1+n2=n1+2n2+1 消去 n 1 n_1 n1: n 0 + n 2 = 2 n 2 + 1 n_0 + n_2 = 2n_2 + 1 n0+n2=2n2+1 移项得: n 0 = n 2 + 1 n_0 = n_2 + 1 n0=n2+1 **证毕。** > **注意** :这个性质告诉我们,**二叉树的叶子结点数只和度为2的结点数有关**,与度为1的结点数无关。 *** ** * ** *** ### 完全二叉树的特有性质 **完全二叉树 (Complete Binary Tree)** 是效率极高的一种特殊二叉树(堆就是完全二叉树),它有两个额外的核心性质。 #### 性质 4:完全二叉树的深度 > **结论** :具有 n n n 个结点的完全二叉树的深度 k k k 为 ⌊ log ⁡ 2 n ⌋ + 1 \\lfloor \\log_2 n \\rfloor + 1 ⌊log2n⌋+1 或 ⌈ log ⁡ 2 ( n + 1 ) ⌉ \\lceil \\log_2(n+1) \\rceil ⌈log2(n+1)⌉。 > *(注: ⌊ x ⌋ \\lfloor x \\rfloor ⌊x⌋ 表示向下取整)* * **证明:** 假设完全二叉树深度为 k k k。 1. 根据性质2,深度为 k k k 的满二叉树有 2 k − 1 2\^k - 1 2k−1 个结点。 2. 深度为 k − 1 k-1 k−1 的满二叉树有 2 k − 1 − 1 2\^{k-1} - 1 2k−1−1 个结点。 3. 完全二叉树的结点数 n n n 介于两者之间(第 k k k 层至少有1个结点,至多排满): 2 k − 1 − 1 \< n ≤ 2 k − 1 2\^{k-1} - 1 \< n \\le 2\^k - 1 2k−1−1\ 0 i \> 0 i\>0,则 i i i 位置结点的双亲(父结点)序号为: ⌊ ( i − 1 ) / 2 ⌋ \\lfloor (i-1)/2 \\rfloor ⌊(i−1)/2⌋; * 若 i = 0 i = 0 i=0, i i i 为根结点编号,无双亲结点。 2. **左孩子序号**: * 若 2 i + 1 \< n 2i + 1 \< n 2i+1\ **正确答案:B** **【详细解析】** 这是一道考察二叉树通用性质的题目。 根据二叉树的性质 n 0 = n 2 + 1 n_0 = n_2 + 1 n0=n2+1(即:叶子结点数 = 度为2的结点数 + 1): * 已知 n 2 = 199 n_2 = 199 n2=199 * 则 n 0 = 199 + 1 = 200 n_0 = 199 + 1 = 200 n0=199+1=200 **验证合法性(排除A):** 总结点数 n = n 0 + n 1 + n 2 n = n_0 + n_1 + n_2 n=n0+n1+n2。 399 = 200 + n 1 + 199 399 = 200 + n_1 + 199 399=200+n1+199 399 = 399 + n 1 ⇒ n 1 = 0 399 = 399 + n_1 \\Rightarrow n_1 = 0 399=399+n1⇒n1=0 度为1的结点数为0,这是一个合法的二叉树(实际上这是一棵满二叉树或正则二叉树)。 故选 **B**。 *** ** * ** *** #### 2. 顺序存储结构的适用性 **题目描述:** 下列数据结构中,不适合采用顺序存储结构的是( ) A. 非完全二叉树 B. 堆 C. 队列 D. 栈 > **正确答案:A** **【详细解析】** * **A. 非完全二叉树** :如果在数组(顺序存储)中存储非完全二叉树,为了保持节点间的父子下标关系(即父节点 i i i 的左孩子为 2 i 2i 2i,右孩子为 2 i + 1 2i+1 2i+1),必须在数组中留出空位来代表"缺失"的节点。这会导致大量的空间浪费(稀疏),因此通常采用**链式存储**。 * **B. 堆**:堆在逻辑上是一棵完全二叉树,在数组中存储时紧凑且无空间浪费,非常适合顺序存储。 * **C. 队列 \& D. 栈**:作为线性表,顺序存储(数组)是它们的标准实现方式之一。 故选 **A**。 *** ** * ** *** #### 3. 完全二叉树的叶子结点推导 **题目描述:** 在具有 2 n 2n 2n 个结点的完全二叉树中,叶子结点个数为( ) A. n n n B. n + 1 n+1 n+1 C. n − 1 n-1 n−1 D. n / 2 n/2 n/2 > **正确答案:A** **【详细解析】** **方法一:利用奇偶性分析** 1. 设总结点数为 N = 2 n N = 2n N=2n,这是一个**偶数**。 2. 我们知道 N = n 0 + n 1 + n 2 N = n_0 + n_1 + n_2 N=n0+n1+n2。 3. 代入性质 n 2 = n 0 − 1 n_2 = n_0 - 1 n2=n0−1,得: N = n 0 + n 1 + ( n 0 − 1 ) = 2 n 0 + n 1 − 1 N = n_0 + n_1 + (n_0 - 1) = 2n_0 + n_1 - 1 N=n0+n1+(n0−1)=2n0+n1−1 4. 在完全二叉树中,度为1的结点数 n 1 n_1 n1 只能是 **0** 或 **1** 。 * 若 n 1 = 0 n_1 = 0 n1=0,则 N = 2 n 0 − 1 N = 2n_0 - 1 N=2n0−1(结果为奇数)。 * 若 n 1 = 1 n_1 = 1 n1=1,则 N = 2 n 0 N = 2n_0 N=2n0(结果为偶数)。 5. 因为题目给出总结点数 2 n 2n 2n 是**偶数** ,所以必然满足 n 1 = 1 n_1 = 1 n1=1 的情况。 6. 方程变为: 2 n = 2 n 0 ⇒ n 0 = n 2n = 2n_0 \\Rightarrow n_0 = n 2n=2n0⇒n0=n。 **方法二:公式法** 对于完全二叉树,叶子结点数 n 0 = ⌈ N / 2 ⌉ n_0 = \\lceil N/2 \\rceil n0=⌈N/2⌉ (向上取整)。 n 0 = ⌈ 2 n / 2 ⌉ = n n_0 = \\lceil 2n / 2 \\rceil = n n0=⌈2n/2⌉=n。 故选 **A**。 *** ** * ** *** #### 4. 完全二叉树的高度计算 **题目描述:** 一棵完全二叉树的结点数位为 531 个,那么这棵树的高度为( ) A. 11 B. 10 C. 8 D. 12 > **正确答案:B** **【详细解析】** 根据完全二叉树高度公式: h = ⌊ log ⁡ 2 N ⌋ + 1 h = \\lfloor \\log_2 N \\rfloor + 1 h=⌊log2N⌋+1。 1. 我们需要找到 531 介于 2 的哪两个幂次方之间。 2. 2 9 = 512 2\^9 = 512 29=512 3. 2 10 = 1024 2\^{10} = 1024 210=1024 4. 因为 512 \< 531 \< 1024 512 \< 531 \< 1024 512\<531\<1024,即 2 9 \< 531 \< 2 10 2\^9 \< 531 \< 2\^{10} 29\<531\<210。 5. 所以 ⌊ log ⁡ 2 531 ⌋ = 9 \\lfloor \\log_2 531 \\rfloor = 9 ⌊log2531⌋=9。 6. 高度 h = 9 + 1 = 10 h = 9 + 1 = 10 h=9+1=10。 故选 **B**。 *** ** * ** *** #### 5. 大规模完全二叉树的叶子计算 **题目描述:** 一个具有 767 个结点的完全二叉树,其叶子结点个数为( ) > **正确答案:384** **【详细解析】** **方法一:奇偶判断法** 1. 总结点数 N = 767 N = 767 N=767 是**奇数**。 2. 根据 N = 2 n 0 + n 1 − 1 N = 2n_0 + n_1 - 1 N=2n0+n1−1,要使 N N N 为奇数,则 n 1 n_1 n1 必须为 0(若 n 1 = 1 n_1=1 n1=1 则 N N N 为偶数)。 3. 所以: 767 = 2 n 0 − 1 767 = 2n_0 - 1 767=2n0−1 4. 2 n 0 = 768 2n_0 = 768 2n0=768 5. n 0 = 384 n_0 = 384 n0=384 **方法二:临界点法** 在完全二叉树中,最后一个非叶子结点的编号为 ⌊ N / 2 ⌋ \\lfloor N/2 \\rfloor ⌊N/2⌋。 * 最后一个非叶子结点索引 = ⌊ 767 / 2 ⌋ = 383 \\lfloor 767 / 2 \\rfloor = 383 ⌊767/2⌋=383。 * 叶子结点就是编号大于 383 的所有结点。 * 叶子数 = 总数 - 非叶子数 = 767 − 383 = 384 767 - 383 = 384 767−383=384。 *** ** * ** *** #### 总结技巧 做二叉树选择题时,牢记以下三个核心工具: * 🚩1. **通用公式** : n 0 = n 2 + 1 n_0 = n_2 + 1 n0=n2+1 (适用所有二叉树) * 🚩2. **完全二叉树判定** : n 1 ∈ { 0 , 1 } n_1 \\in \\{0, 1\\} n1∈{0,1},且 N N N 为偶数时 n 1 = 1 n_1=1 n1=1,N为奇数时 n 1 = 0 n_1=0 n1=0。 * 🚩3. **高度估算** :熟记 2 10 = 1024 2\^{10}=1024 210=1024, 2 8 = 256 2\^8=256 28=256 等关键数值。 *** ** * ** *** ## 🤔五、二叉树的存储结构 二叉树一般可以使用两种结构存储,一种`顺序结构`,一种`链式结构` > 普通的二叉树是不适合用**数组** 来存储的,因为可能会存在大量的空间浪费 > > 而完全二叉树更适合使用**顺序结构** 存储 > ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/b8b0af591400489097958496c82b12e7.png) > > **现实中我们通常把`堆(一种二叉树)`使用`顺序结构的数组`来存储** * 对于**完全二叉树**,我们可以直接将其节点按层序编号,存储在数组中 * 对于**一般二叉树** ,为了保持节点间的逻辑关系,我们需要**在数组中将其"补"成完全二叉树,不存在的节点在数组中用"空"(如 0 或特殊符号)表示** *** ** * ** *** #### 5.1)顺序存储 🚩二叉树的顺序存储结构是指利用**一组地址连续的存储单元** (通常是**数组** )依次`自上而下`、`自左至右`存储完全二叉树上的结点元素 > 现实中我们通常把 **堆(一种二叉树)** 使用`顺序结构的数组`来存储 > > 🔗[Lucy的空间骇客裂缝:堆](https://blog.csdn.net/fengtinghuqu520/article/details/156204209) 非完全二叉树不适合用`顺序存储`,因为会**造成大量的空间浪费** ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/b3cacdfb3ea84da09ec784ce9665074e.png) *** ** * ** *** #### 5.2)链式存储 * **链式结构节点的定义** ```c typedef int BTDataType; typedef struct BinaryTreeNode { BTDataType data; struct BinaryTreeNode* right; struct BinaryTreeNode* left; }BTNode; ``` 🚩`data`------*当前节点的值域* 🚩`left`------*左孩子节点* 🚩`right`------*右孩子节点* *** ** * ** *** ##### 5.2.1)二叉树的遍历方式 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/ca52bd4178db494281a0a5dbee77b530.png) ###### 1)前序遍历 先遍历根节点,再遍历左子树,最后遍历右子树---**根左右** ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/e919ff1934214765bdc2eac434b11e56.png) A B D NULL NULL NULL C E NULL NULL F NULL NULL *** ** * ** *** ###### 2)中序遍历 先遍历左子树,再遍历根节点,最后遍历右子树---**左根右** ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/bae4417713ae4c119672df26d714a712.png) NULL D NULL B NULL A NULL E NULL C NULL F NULL *** ** * ** *** ###### 3)后序遍历 先遍历左子树,再遍历右子树,最后遍历根节点---**左右根** ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/70f7e1f8811344fc9f15e674d4b856b0.png) NULL NULL D NULL B NULL NULL E NULL NULL F C A *** ** * ** *** ###### 4)层序遍历 按照层次依次遍历 **(从上到下,从左到右)** 除了**先序遍历、中序遍历、后序遍历** 外,还可以对二叉树进行**层序遍历** > ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/9aebce9f02c84737af49f969b7f2d024.png) > **设二叉树的根结点所在层数为1** > > * 层序遍历就是**从所在二叉树的根结点出发** > * **首先**访问第一层的树根结点 > * **然后**从左到右访问第2层上的结点 > * **接着**是第三层的结点 > * 以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历 *** ** * ** *** ##### 5.2.2)🔨二叉树遍历的经典例题 ###### 🎯 第一题:完全二叉树的序列转化 * **📝 题目描述** > **某完全二叉树按层次输出(同一层从左到右)的序列为 `ABCDEFGH` > 该完全二叉树的前序序列为( )** > > A. ABDHECFG > > B. ABCDEFGH > > C. HDBEAFCG > > D. HDEBFGCA *** ** * ** *** 💡 深度解析 **解题核心:完全二叉树的数组下标性质** 完全二叉树最强大的性质在于它可以用数组完美存储。我们可以给层次序列中的每个节点打上编号(从 1 开始): **Step 1:建立索引映射** 序列 `A B C D E F G H` 对应下标 `1 2 3 4 5 6 7 8`。 **Step 2:利用公式找孩子** 对于完全二叉树中任意节点 i i i: * **左孩子索引** : 2 i 2i 2i * **右孩子索引** : 2 i + 1 2i + 1 2i+1 具体推导如下: * **A (index 1)** → \\rightarrow → 左: 2 2 2 (B), 右: 3 3 3 © * **B (index 2)** → \\rightarrow → 左: 4 4 4 (D), 右: 5 5 5 (E) * **C (index 3)** → \\rightarrow → 左: 6 6 6 (F), 右: 7 7 7 (G) * **D (index 4)** → \\rightarrow → 左: 8 8 8 (H), 右: 9 9 9 (越界,无右孩子) * **E, F, G, H** → \\rightarrow → 均为叶子节点 **Step 3:构建逻辑树(Mermaid图解)** A B C D E F G H *** ** * ** *** ###### 🎯 第二题:秒杀根节点(送分题) * **📝 题目描述** > **二叉树的先序遍历和中序遍历如下:先序遍历 `EFHIGJK`,中序遍历 `HFIEJKG`。则二叉树根结点为()** > > A. E > > B. F > > C. G > > D. H *** ** * ** *** **💡 深度解析** **解题核心:先序遍历的"排头兵"定律** 这道题考察的是对遍历定义的最基本理解,属于"眼疾手快"的送分题。 1. **先序遍历(Pre-order)定义** : Root → Left → Right \\text{Root} \\rightarrow \\text{Left} \\rightarrow \\text{Right} Root→Left→Right 这行公式告诉我们一个铁律:**先序序列的第一个元素,永远是整棵树的根节点**。 2. **解题步骤**: * 👀 **看先序序列** :`E F H I G J K` * 👉 **锁定首位** :第一个字母是 **E**。 * ✅ **得出结论**:整棵树的根节点就是 E。 3. **验算(确保万无一失)** : 在中序序列 `H F I E J K G` 中,我们找到 `E`。 * `E` 左边是 `HFI`(左子树节点) * `E` 右边是 `JKG`(右子树节点) * 逻辑完全通畅,答案无误。 ✅ 正确答案:A *** ** * ** *** ###### 🎯 第三题:由中序与后序还原前序(经典难点) * **📝 题目描述** **设一棵二叉树的中序遍历序列:`badce`,后序遍历序列:`bdeca`,则二叉树前序遍历序列为____。** > A. adbce > > B. decab > > C. debac > > D. abcde *** ** * ** *** 💡 深度解析 **解题核心:后序定根,中序分左右** 这是二叉树考题中的"重头戏",需要利用递归思想进行还原。 **Step 1:确定全局根节点** * **后序序列** (Left → \\rightarrow → Right → \\rightarrow → Root):`b d e c` **`a`** * 🎯 序列最后一个元素是 **a** ,所以 **Root = a**。 **Step 2:第一轮切分(中序序列)** * 在**中序序列** (Left → \\rightarrow → Root → \\rightarrow → Right)中找到 `a`:`b` **`a`** `d c e` * 👈 **左子树** :`b`(只有一个节点,搞定) * 👉 **右子树** :`d c e`(这就需要进行下一轮分析) **Step 3:深入分析右子树** 我们需要确定右子树 `{d, c, e}` 的内部结构: 1. **找右子树的根** :看后序序列中对应这三个节点的部分 → \\rightarrow → `d e c`。 2. **锁定** :后序 `d e` **`c`** 的最后一个是 **c** 。所以,**右子树的根是 c**。 3. **切分右子树** :回到中序序列的右边部分 `d c e`,以 `c` 为中心切分: * `c` 左边是 `d` → \\rightarrow → **c 的左孩子是 d** * `c` 右边是 `e` → \\rightarrow → **c 的右孩子是 e** **Step 4:构建完整的逻辑结构图** a b c d e *** ** * ** *** ## 🤔六、二叉树的分文件编写 > 🔗[Lucy的gitee码云传送门:链式二叉树](https://gitee.com/maple-lake-district/coding/commit/891746b332dc76f50b539d89e89aed3eb0536fc5) ### BinaryTree.h ```c #pragma once #include #include #include #include // 假设树中存储的数据是 int 类型 typedef int BTDataType; typedef struct BinaryTreeNode { BTDataType data; struct BinaryTreeNode* left; struct BinaryTreeNode* right; } BTNode; // --- 核心功能接口 --- // 创建一个新节点 BTNode* BinaryTreeCreateNode(BTDataType x); // 通过前序遍历的数组构建二叉树 (假设 '#' 或特定值代表空) // 注意:这里为了通用性,我们在.c中定义 -1 代表空节点 BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi); // 二叉树的销毁 void BinaryTreeDestory(BTNode** root); // 二叉树的节点个数 int BinaryTreeSize(BTNode* root); // 二叉树的叶子节点个数 int BinaryTreeLeafSize(BTNode* root); // 二叉树第k层节点个数 int BinaryTreeLevelKSize(BTNode* root, int k); // 二叉树查找值为x的节点 BTNode* BinaryTreeFind(BTNode* root, BTDataType x); // 二叉树的前序遍历 void PreOrder(BTNode* root); // 二叉树的中序遍历 void InOrder(BTNode* root); // 二叉树的后序遍历 void PostOrder(BTNode* root); // 二叉树的层序遍历 void LevelOrder(BTNode* root); // 判断二叉树是否为完全二叉树 int BinaryTreeComplete(BTNode* root); // 获取二叉树的高度/深度 int BinaryTreeHeight(BTNode* root); ``` *** ** * ** *** ### BinaryTree.c **这里实现了所有逻辑,并包含了一个简易队列用于辅助层序遍历** *** ** * ** *** #### 简易队列的创建(用于 LevelOrder 和 BinaryTreeComplete) > 涉及一些**队列数据结构** 的知识👇 > > 🔗[Lucy的空间骇客裂缝:栈与队列](https://blog.csdn.net/fengtinghuqu520/article/details/155920754) *** ** * ** *** ##### 1)结构体的创建 **注意队列里面存储的是树节点的指针** ```c typedef BTNode* QDataType; // 队列里存的是树节点的指针 ``` *** ** * ** *** ```c typedef struct QueueNode { QDataType data; struct QueueNode* next; } QNode; typedef struct Queue { QNode* head; QNode* tail; } Queue; ``` *** ** * ** *** ##### 2)初始化队列 ```c void QueueInit(Queue* pq) { assert(pq); pq->head = pq->tail = NULL; } ``` *** ** * ** *** ##### 3)入队 ```c void QueuePush(Queue* pq, QDataType x) { assert(pq); QNode* newnode = (QNode*)malloc(sizeof(QNode)); if (newnode == NULL) { perror("malloc fail"); exit(-1); } newnode->data = x; newnode->next = NULL; if (pq->tail == NULL) { pq->head = pq->tail = newnode; } else { pq->tail->next = newnode; pq->tail = newnode; } } ``` *** ** * ** *** ##### 4)出队 ```c void QueuePop(Queue* pq) { assert(pq); assert(pq->head); // 确保队列不为空 QNode* next = pq->head->next; free(pq->head); pq->head = next; if (pq->head == NULL) { pq->tail = NULL; } } ``` *** ** * ** *** ##### 5)获取队头元素 ```c QDataType QueueFront(Queue* pq) { assert(pq); assert(pq->head); return pq->head->data; } ``` *** ** * ** *** ##### 6)判断队列是否为空 ```c bool QueueEmpty(Queue* pq) { assert(pq); return pq->head == NULL; } ``` *** ** * ** *** ##### 7)销毁队列 ```c void QueueDestroy(Queue* pq) { assert(pq); QNode* cur = pq->head; while (cur) { QNode* next = cur->next; free(cur); cur = next; } pq->head = pq->tail = NULL; } ``` *** ** * ** *** #### 🌠创建新节点 ```c BTNode* BinaryTreeCreateNode(BTDataType x) { BTNode* node = (BTNode*)malloc(sizeof(BTNode)); if (node == NULL) { perror("malloc fail"); exit(-1); } node->data = x; node->left = NULL; node->right = NULL; return node; } ``` **内存分配** **`BTNode* node = (BTNode*)malloc(sizeof(BTNode));`** *** ** * ** *** #### 🌠通过前序数组构建二叉树 ```c //约定:数组中-1代表NULL BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi) { if (*pi >= n) return NULL; if (a[*pi] == -1) { (*pi)++; return NULL; } BTNode* root = BinaryTreeCreateNode(a[*pi]); (*pi)++; root->left = BinaryTreeCreate(a, n, pi); root->right = BinaryTreeCreate(a, n, pi); return root; } ``` * **`int* pi`** :指向**当前数组索引的指针** (必须用**指针** ,**保证递归中索引同步递增**) * **`return NULL;`** :返回`NULL`,告诉父节点这个子节点为空 * **`BTNode* root = BinaryTreeCreateNode(a[*pi]);`** :用当前数组元素`a[*pi]`的值创建节点 **递归创建左右子树** `root->left = BinaryTreeCreate(a, n, pi);` `root->right = BinaryTreeCreate(a, n, pi);` *** ** * ** *** #### 🌠二叉树销毁 (后序遍历销毁) ```c void BinaryTreeDestory(BTNode** root) { assert(root); if (*root == NULL) return; BinaryTreeDestory(&((*root)->left)); BinaryTreeDestory(&((*root)->right)); free(*root); *root = NULL; // 这是一个好习惯,防止野指针 } ``` **二级指针** **`BTNode** root`** :传入**根节点的地址(指针的地址)** ,遍历整棵树并释放所有节点的内存,最后将根指针设置为 `NULL` *** ** * ** *** #### 🌠节点个数 ```c int BinaryTreeSize(BTNode* root) { return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1; } ``` **`int`和`BTDataType`的区别** * `int`表示真实数据个数 * `BTDataType`表示二叉树内部的数据 *** ** * ** *** #### 🌠叶子节点个数(递归实现) ```c 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); } ``` Pass Yes No Yes No Start: BinaryTreeLevelKSize Assert: k \>= 1 root == NULL ? k == 1 ? Return 0 Return 1 Return Sum int left = BinaryTreeLevelKSize(root-\>left, k - 1) int right = BinaryTreeLevelKSize(root-\>right, k - 1) Result = left + right *** ** * ** *** #### 🌠第k层节点个数 ```c int BinaryTreeLevelKSize(BTNode* root, int k) { assert(k >= 1); if (root == NULL) return 0; if (k == 1) return 1; return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1); } ``` **递归运算** `return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);` 时间复杂度:O(k) *** ** * ** *** #### 🌠查找值为x的节点 ```c BTNode* BinaryTreeFind(BTNode* root, BTDataType x) { if (root == NULL) return NULL; if (root->data == x) return root; BTNode* ret1 = BinaryTreeFind(root->left, x); if (ret1) return ret1; // 左边找到了就返回,不用找右边了 BTNode* ret2 = BinaryTreeFind(root->right, x); if (ret2) return ret2; return NULL; } ``` *** ** * ** *** #### 🌠前序遍历: 根 -\> 左 -\> 右 ```c void PreOrder(BTNode* root) { if (root == NULL) { printf("NULL "); return; } printf("%d ", root->data); PreOrder(root->left); PreOrder(root->right); } ``` *** ** * ** *** #### 🌠中序遍历: 左 -\> 根 -\> 右 ```c void InOrder(BTNode* root) { if (root == NULL) { printf("NULL "); return; } InOrder(root->left); printf("%d ", root->data); InOrder(root->right); } ``` *** ** * ** *** #### 🌠后序遍历: 左 -\> 右 -\> 根 ```c void PostOrder(BTNode* root) { if (root == NULL) { printf("NULL "); return; } PostOrder(root->left); PostOrder(root->right); printf("%d ", root->data); } ``` *** ** * ** *** #### 🌠层序遍历 (使用队列) ```c void LevelOrder(BTNode* root) { if (root == NULL) return; Queue q; QueueInit(&q); QueuePush(&q, root); while (!QueueEmpty(&q)) { BTNode* front = QueueFront(&q); QueuePop(&q); printf("%d ", front->data); if (front->left) QueuePush(&q, front->left); if (front->right) QueuePush(&q, front->right); } printf("\n"); QueueDestroy(&q); } ``` *** ** * ** *** #### 🌠判断是否为完全二叉树 **核心逻辑:** 层序遍历,一旦遇到空节点,后续队列中不能再出现非空节点 ```c int BinaryTreeComplete(BTNode* root) { if (root == NULL) return 1; Queue q; QueueInit(&q); QueuePush(&q, root); while (!QueueEmpty(&q)) { BTNode* front = QueueFront(&q); QueuePop(&q); if (front == NULL) { break; // 遇到了空节点,跳出这一层循环去检查剩余队列 } // 不管孩子是不是空,都入队(为了检测是否断层) QueuePush(&q, front->left); QueuePush(&q, front->right); } // 检查队列剩余元素,如果有非空,则不是完全二叉树 while (!QueueEmpty(&q)) { BTNode* front = QueueFront(&q); QueuePop(&q); if (front != NULL) { QueueDestroy(&q); return 0; // 失败 } } QueueDestroy(&q); return 1; // 成功 } ``` *** ** * ** *** #### 🌠二叉树的高度 ```c int BinaryTreeHeight(BTNode* root) { if (root == NULL) return 0; int leftHeight = BinaryTreeHeight(root->left); int rightHeight = BinaryTreeHeight(root->right); return (leftHeight > rightHeight ? leftHeight : rightHeight) + 1; } ``` *** ** * ** *** #### 🖊完整代码 ```c #include "BinaryTree.h" // ========================================== // 辅助模块:简易队列 (Queue) 实现 // 用于 LevelOrder 和 BinaryTreeComplete // ========================================== typedef BTNode* QDataType; // 队列里存的是树节点的指针 typedef struct QueueNode { QDataType data; struct QueueNode* next; } QNode; typedef struct Queue { QNode* head; QNode* tail; } Queue; // 初始化队列 void QueueInit(Queue* pq) { assert(pq); pq->head = pq->tail = NULL; } // 入队 void QueuePush(Queue* pq, QDataType x) { assert(pq); QNode* newnode = (QNode*)malloc(sizeof(QNode)); if (newnode == NULL) { perror("malloc fail"); exit(-1); } newnode->data = x; newnode->next = NULL; if (pq->tail == NULL) { pq->head = pq->tail = newnode; } else { pq->tail->next = newnode; pq->tail = newnode; } } // 出队 void QueuePop(Queue* pq) { assert(pq); assert(pq->head); // 确保队列不为空 QNode* next = pq->head->next; free(pq->head); pq->head = next; if (pq->head == NULL) { pq->tail = NULL; } } // 获取队头元素 QDataType QueueFront(Queue* pq) { assert(pq); assert(pq->head); return pq->head->data; } // 判断队列是否为空 bool QueueEmpty(Queue* pq) { assert(pq); return pq->head == NULL; } // 销毁队列 void QueueDestroy(Queue* pq) { assert(pq); QNode* cur = pq->head; while (cur) { QNode* next = cur->next; free(cur); cur = next; } pq->head = pq->tail = NULL; } // ========================================== // 二叉树核心功能实现 // ========================================== // 创建新节点 BTNode* BinaryTreeCreateNode(BTDataType x) { BTNode* node = (BTNode*)malloc(sizeof(BTNode)); if (node == NULL) { perror("malloc fail"); exit(-1); } node->data = x; node->left = NULL; node->right = NULL; return node; } // 通过前序数组构建二叉树 // 约定:数组中 -1 代表 NULL BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi) { if (*pi >= n) return NULL; // 这里的 -1 是我们约定的空节点值 if (a[*pi] == -1) { (*pi)++; return NULL; } BTNode* root = BinaryTreeCreateNode(a[*pi]); (*pi)++; root->left = BinaryTreeCreate(a, n, pi); root->right = BinaryTreeCreate(a, n, pi); return root; } // 二叉树销毁 (后序遍历销毁) void BinaryTreeDestory(BTNode** root) { assert(root); if (*root == NULL) return; BinaryTreeDestory(&((*root)->left)); BinaryTreeDestory(&((*root)->right)); free(*root); *root = NULL; // 这是一个好习惯,防止野指针 } // 节点个数 int BinaryTreeSize(BTNode* root) { return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1; } // 叶子节点个数 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); } // 第k层节点个数 int BinaryTreeLevelKSize(BTNode* root, int k) { assert(k >= 1); if (root == NULL) return 0; if (k == 1) return 1; // 子问题的思路:求左树的k-1层 + 右树的k-1层 return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1); } // 查找值为x的节点 BTNode* BinaryTreeFind(BTNode* root, BTDataType x) { if (root == NULL) return NULL; if (root->data == x) return root; BTNode* ret1 = BinaryTreeFind(root->left, x); if (ret1) return ret1; // 左边找到了就返回,不用找右边了 BTNode* ret2 = BinaryTreeFind(root->right, x); if (ret2) return ret2; return NULL; } // 前序遍历: 根 -> 左 -> 右 void PreOrder(BTNode* root) { if (root == NULL) { printf("NULL "); return; } printf("%d ", root->data); PreOrder(root->left); PreOrder(root->right); } // 中序遍历: 左 -> 根 -> 右 void InOrder(BTNode* root) { if (root == NULL) { printf("NULL "); return; } InOrder(root->left); printf("%d ", root->data); InOrder(root->right); } // 后序遍历: 左 -> 右 -> 根 void PostOrder(BTNode* root) { if (root == NULL) { printf("NULL "); return; } PostOrder(root->left); PostOrder(root->right); printf("%d ", root->data); } // 层序遍历 (使用队列) void LevelOrder(BTNode* root) { if (root == NULL) return; Queue q; QueueInit(&q); QueuePush(&q, root); while (!QueueEmpty(&q)) { BTNode* front = QueueFront(&q); QueuePop(&q); printf("%d ", front->data); if (front->left) QueuePush(&q, front->left); if (front->right) QueuePush(&q, front->right); } printf("\n"); QueueDestroy(&q); } // 判断是否为完全二叉树 // 核心逻辑:层序遍历,一旦遇到空节点,后续队列中不能再出现非空节点 int BinaryTreeComplete(BTNode* root) { if (root == NULL) return 1; Queue q; QueueInit(&q); QueuePush(&q, root); while (!QueueEmpty(&q)) { BTNode* front = QueueFront(&q); QueuePop(&q); if (front == NULL) { break; // 遇到了空节点,跳出这一层循环去检查剩余队列 } // 不管孩子是不是空,都入队(为了检测是否断层) QueuePush(&q, front->left); QueuePush(&q, front->right); } // 检查队列剩余元素,如果有非空,则不是完全二叉树 while (!QueueEmpty(&q)) { BTNode* front = QueueFront(&q); QueuePop(&q); if (front != NULL) { QueueDestroy(&q); return 0; // 失败 } } QueueDestroy(&q); return 1; // 成功 } // 二叉树的高度 int BinaryTreeHeight(BTNode* root) { if (root == NULL) return 0; int leftHeight = BinaryTreeHeight(root->left); int rightHeight = BinaryTreeHeight(root->right); return (leftHeight > rightHeight ? leftHeight : rightHeight) + 1; } ``` *** ** * ** *** ### test.c ```c #include "BinaryTree.h" // 测试手动构建和基本遍历 void TestBinaryTree1() { printf("====== Test 1: 手动构建树 ======\n"); // 手动构建一个简单的树: // 1 // / \ // 2 3 // / \ // 4 5 BTNode* n1 = BinaryTreeCreateNode(1); BTNode* n2 = BinaryTreeCreateNode(2); BTNode* n3 = BinaryTreeCreateNode(3); BTNode* n4 = BinaryTreeCreateNode(4); BTNode* n5 = BinaryTreeCreateNode(5); n1->left = n2; n1->right = n3; n2->left = n4; n2->right = n5; printf("前序遍历: "); PreOrder(n1); // 1 2 4 NULL NULL 5 NULL NULL 3 NULL NULL printf("\n"); printf("中序遍历: "); InOrder(n1); printf("\n"); printf("后序遍历: "); PostOrder(n1); printf("\n"); printf("层序遍历: "); LevelOrder(n1); // 1 2 3 4 5 printf("节点个数: %d\n", BinaryTreeSize(n1)); // 5 printf("叶子节点: %d\n", BinaryTreeLeafSize(n1)); // 3 (4, 5, 3) printf("高度: %d\n", BinaryTreeHeight(n1)); // 3 printf("第3层节点数: %d\n", BinaryTreeLevelKSize(n1, 3)); // 2 (4, 5) BTNode* find = BinaryTreeFind(n1, 5); printf("查找值为5的节点: %p, 值: %d\n", find, find ? find->data : -1); printf("是否完全二叉树? %s\n", BinaryTreeComplete(n1) ? "YES" : "NO"); // YES BinaryTreeDestory(&n1); printf("销毁后根节点指针: %p\n", n1); } // 测试数组构建和完全二叉树判断 void TestBinaryTree2() { printf("\n====== Test 2: 数组构建与完全二叉树判断 ======\n"); // 构建一棵树: 1 2 3 # # # 4 5 # # 6 # # // 注意:这里的 # 用 -1 表示 // 1 // / \ // 2 4 // / / \ // 3 5 6 int arr[] = { 1, 2, 3, -1, -1, -1, 4, 5, -1, -1, 6, -1, -1 }; int i = 0; BTNode* root = BinaryTreeCreate(arr, sizeof(arr) / sizeof(arr[0]), &i); printf("前序遍历检查: "); PreOrder(root); printf("\n"); printf("层序遍历: "); LevelOrder(root); // 这个结构不是完全二叉树,因为2号节点的右孩子为空,但4号节点存在 printf("是否完全二叉树? %s\n", BinaryTreeComplete(root) ? "YES" : "NO"); // Should be NO printf("树的高度: %d\n", BinaryTreeHeight(root)); // 3 BinaryTreeDestory(&root); } int main() { TestBinaryTree1(); TestBinaryTree2(); return 0; } ``` *** ** * ** *** ## 🤔关于二叉树的在线OJ题 > [单值二叉树](https://leetcode.cn/problems/univalued-binary-tree/description/) > [相同的树](https://leetcode.cn/problems/same-tree/description/) > [对称二叉树](https://leetcode.cn/problems/symmetric-tree/description/) > [二叉树的前序遍历](https://leetcode.cn/problems/binary-tree-preorder-traversal/description/) > [二叉树的中序遍历](https://leetcode.cn/problems/binary-tree-inorder-traversal/description/) > [二叉树的后序遍历](https://leetcode.cn/problems/binary-tree-postorder-traversal/description/) > [另一棵树的子树](https://leetcode.cn/problems/subtree-of-another-tree/description/) *** ** * ** *** ## 💻结尾--- 核心连接协议 **警告:** 🌠🌠正在接入底层技术矩阵。如果你已成功破解学习中的逻辑断层,请执行以下指令序列以同步数据:🌠🌠 *** ** * ** *** **【📡】 建立深度链接:** **关注**本终端。在赛博丛林中深耕底层架构,从原始代码到进阶协议,同步见证每一次系统升级。 **【⚡】 能量过载分发:** 执行**点赞**操作。通过高带宽分发,让优质模组在信息流中高亮显示,赋予知识跨维度的传播力。 **【💾】 离线缓存核心:** 将本页加入**收藏**。把这些高频实战逻辑存入你的离线存储器,在遭遇系统崩溃或需要离线检索时,实现瞬时读取。 **【💬】 协议加密解密:** 在**评论区**留下你的散列码。分享你曾遭遇的代码冲突或系统漏洞(那些年踩过的坑),通过交互式编译共同绕过技术陷阱。 **【🛰️】 信号频率投票:** 通过**投票**发射你的选择。你的每一次点击都在重新定义矩阵的进化方向,决定下一个被全量拆解的技术节点。 *** ** * ** *** ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/57b03915c54b43a7a03fa92dbbfe57c3.gif) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/0905dc972de8414bb602715de3f866ee.gif)

相关推荐
fpcc2 小时前
C++编程实践—false_type和true_type的实践应用
c++
量子炒饭大师3 小时前
Cyber骇客神经塔尖协议 ——【初阶数据结构与算法】堆
c语言·数据结构·c++·二叉树·github·
XLYcmy3 小时前
TarGuessIRefined密码生成器详细分析
开发语言·数据结构·python·网络安全·数据安全·源代码·口令安全
王老师青少年编程3 小时前
2025年12月GESP(C++二级): 环保能量球
c++·算法·gesp·csp·信奥赛·二级·环保能量球
KingRumn3 小时前
DBUS源码剖析之DBusMessage数据结构
linux·服务器·数据结构
CoderCodingNo4 小时前
【GESP】C++五级真题(贪心思想考点) luogu-P11960 [GESP202503 五级] 平均分配
开发语言·c++·算法
Herbert_hwt4 小时前
C语言赋值操作符详解:从基础使用到避坑指南
c语言
youngee114 小时前
hot100-61电话号码的字母组合
java·数据结构·leetcode
jackyrongvip4 小时前
10个动画介绍递归(用Gemin3生成)
数据结构·递归·gemin3