【数据结构】二叉树详解:从基础概念到高频考点全覆盖(全代码逐行解析)
大家好,今天我们来系统学习二叉树这一重要数据结构。
二叉树是计算机科学中最核心的概念之一,无论是后续学习算法(如搜索树、平衡树、红黑树),还是应对考研/求职面试,都是必须掌握的内容。本文将带你从零开始,系统理解二叉树的基本概念 、链式实现 、核心操作 以及OJ高频考题,每一段代码都讲清楚"为什么这样写"。
目录
- 一、二叉树基本概念
- 二、二叉树的存储结构
- 三、二叉树链式结构的基本操作
- 四、二叉树的四种遍历方式
- 五、由遍历序列还原二叉树
- 六、结点的几个计数问题
- 七、二叉树的查找
- 八、层序遍历
- 九、判断是否是完全二叉树
- 十、二叉树的销毁
- 十一、LeetCode高频OJ题详解
- 十二、课后习题详解
一、二叉树基本概念
1.1 树的概念与术语
树是一种非线性 数据结构,由 n(n≥0)个有限结点组成一个具有层次关系的集合。
关键术语:
| 术语 | 含义 |
|---|---|
| 根结点 | 没有前驱结点的结点 |
| 叶子结点 | 度为0的结点 |
| 分支结点 | 度不为0的结点 |
| 父结点/双亲 | 若一个结点有子结点,它是子结点的父结点 |
| 孩子结点 | 一个结点含有的子树的根结点 |
| 兄弟结点 | 具有相同父结点的结点 |
| 树的度 | 树中最大的结点度 |
| 树的深度/高度 | 树中结点的最大层次数 |
1.2 二叉树是什么
一棵二叉树是结点的一个有限集合:要么为空 ,要么由一个根结点 + 左子树 + 右子树组成。
二叉树的特点:每个结点最多只有两个子树,且左右次序不能颠倒。
1.3 两种特殊的二叉树
满二叉树:每一层的结点数都达到最大值。层数为K时,结点总数 = 2^K - 1。
A
/ \
B C
/ \ / \
D E F G
↑ 满二叉树(K=3,7个结点)
完全二叉树:前 K-1 层都是满的,第 K 层结点从左到右连续排列。
1.4 二叉树的性质(重要!)
性质1:第 i 层最多有 2^(i-1) 个结点
性质2:深度为 h 的二叉树最多有 2^h - 1 个结点
性质3 :对任何二叉树,n₀ = n₂ + 1(叶子数 = 度为2的结点数 + 1)
这里可以用用图的方法证明一下
性质3n₀ = n₂ + 1的证明

我们可以看见每一个节点数会比边多一个,这个是我们证明最重要的结论
通过这个我们发现n0*0+n1*1+n2*2+1=n0+n1+n2
化简的n2+1=n1得证。
关于性质3n₀ = n₂ + 1结论的应用
我们可以通过下列题目巩固一下
- 某二叉树共有 399 个结点,其中有 199 个度为 2 的结点,则该二叉树中的叶子结点数为(B )
A 不存在这样的二叉树
B 200
C 198
D 199
由n0=n2+1;
代入的n0=200;
2.在具有 2n 个结点的完全二叉树中,叶子结点个数为( A)
A n
B n+1
C n-1
D n/2
这题我们也带入公式n0=n2+1;
得到n0+n0-1+n1=2n;
这里我们要注意完全二叉树,n1最多为1;
我们由上述可知n0为偶数,所以n0等于1,n0=n;
性质4:具有 n 个结点的满二叉树,深度 h = log₂(n+1)
性质5:完全二叉树按层序编号(从0开始):
- 双亲序号:(i-1)/2
- 左孩子:2i+1
- 右孩子:2i+2
二、二叉树的存储结构
2.1 顺序存储(数组)
顺序存储适合完全二叉树(尤其是堆)。如果不是完全二叉树,数组会有大量空间浪费。
2.2 链式存储(二叉链)
c
typedef int BTDataType;
typedef struct BinaryTreeNode {
BTDataType _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
} BTNode;
三、二叉树链式结构的基本操作
3.1 创建结点 BuyNode
c
BTNode* BuyNode(BTDataType x)
{
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
if (node == NULL) // ⚠️ 注意:判断是 == ,不是 =
{
perror("fail malloc()");
return NULL;
}
node->_data = x;
node->_left = NULL;
node->_right = NULL;
return node;
}
3.2 手动创建二叉树 CreatBinaryTree
1
/ \
2 4
/ / \
3 5 6
这里我用直接手动创建的方式给大家写一下。
c
BTNode* CreatBinaryTree()
{
BTNode* node1 = BuyNode(1);
BTNode* node2 = BuyNode(2);
BTNode* node3 = BuyNode(3);
BTNode* node4 = BuyNode(4);
BTNode* node5 = BuyNode(5);
BTNode* node6 = BuyNode(6);
node1->_left = node2;
node1->_right = node4;
node2->_left = node3;
node4->_left = node5;
node4->_right = node6;
return node1;
}
四、二叉树的四种遍历方式
4.1 前序遍历(PreOrder)------ 根 → 左 → 右
c
void PrevOrder(BTNode* root)
{
if (root == NULL) {
printf("N ");
return;
}
printf("%d ", root->_data); // 1. 先访问根
PrevOrder(root->_left); // 2. 递归遍历左子树
PrevOrder(root->_right); // 3. 递归遍历右子树
}
4.2 中序遍历(InOrder)------ 左 → 根 → 右
c
void InOrder(BTNode* root)
{
if (root == NULL) {
printf("N ");
return;
}
InOrder(root->_left); // 1. 先递归遍历左子树
printf("%d ", root->_data); // 2. 再访问根
InOrder(root->_right); // 3. 最后递归遍历右子树
}
4.3 后序遍历(PostOrder)------ 左 → 右 → 根
c
void PostOrder(BTNode* root)
{
if (root == NULL) {
printf("N ");
return;
}
PostOrder(root->_left); // 1. 先递归遍历左子树
PostOrder(root->_right); // 2. 再递归遍历右子树
printf("%d ", root->_data); // 3. 最后访问根
}
4.4 层序遍历(LevelOrder)
核心:队列的FIFO特性,逐层从左到右访问。
c
void TreeleverOrder(BTNode* root)
{
assert(root);
QNode q;
QueueInit(&q);
if (root)
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);
}
}
执行流程(以上图树为例):
| 步骤 | 操作 | 队列 | 输出 |
|---|---|---|---|
| 1 | 根1入队 | [1] | --- |
| 2 | 1出队,左2右4入队 | [2,4] | 1 |
| 3 | 2出队,左3入队 | [4,3] | 1 2 |
| 4 | 4出队,左5右6入队 | [3,5,6] | 1 2 4 |
| 5 | 3出队 | [5,6] | 1 2 4 3 |
| 6 | 5出队 | [6] | 1 2 4 3 5 |
| 7 | 6出队 | [] | 1 2 4 3 5 6 |
层序结果:1 2 4 3 5 6
这里的核心就是当父亲节出队列的时候,会把他的孩子带进去,由于是父亲先入的,队列的逻辑是先进先出,所以父亲会先出来,层序遍历就是先让父亲节点入,再带动儿子节点入,达到一层出带动一层入的效果,这就是层序遍历的核心。
4.5 递归展开图详解
以前序遍历为例,树结构:
1
/ \
2 4
/ / \
3 5 6
前序递归展开过程:
调用 PrevOrder(1)
│
├─ 1不为空 → print("1")
│
├─ 调用 PrevOrder(2)
│ │
│ ├─ 2不为空 → print("2")
│ │
│ ├─ 调用 PrevOrder(3)
│ │ │
│ │ ├─ 3不为空 → print("3")
│ │ ├─ 调用 PrevOrder(NULL) → print("N") → 返回
│ │ ├─ 调用 PrevOrder(NULL) → print("N") → 返回
│ │ └─ 返回
│ │
│ ├─ 调用 PrevOrder(NULL) → print("N") → 返回
│ └─ 返回
│
├─ 调用 PrevOrder(4)
│
├─ 4不为空 → print("4")
│
├─ 调用 PrevOrder(5)
│ ├─ 5不为空 → print("5")
│ ├─ 调用 PrevOrder(NULL) → print("N") → 返回
│ ├─ 调用 PrevOrder(NULL) → print("N") → 返回
│ └─ 返回
│
├─ 调用 PrevOrder(6)
├─ 6不为空 → print("6")
├─ 调用 PrevOrder(NULL) → print("N") → 返回
├─ 调用 PrevOrder(NULL) → print("N") → 返回
└─ 返回
访问顺序:1 → 2 → 3 → 4 → 5 → 6
三种遍历结果:
- 前序(根左右):1 2 3 4 5 6
- 中序(左根右):3 2 1 5 4 6
- 后序(左右根):3 2 5 6 4 1
五、由遍历序列还原二叉树
5.1 核心原理
已知中序序列 + 任意一种其他序列,可以唯一确定二叉树。
| 序列 | 作用 |
|---|---|
| 先序 | 每一棵子树的根结点(第一个元素) |
| 后序 | 每一棵子树的根结点(最后一个元素) |
| 中序 | 根结点左右两侧分别是左右子树 |
由先序/后序找根,由中序找左右。
5.2 已知先序+中序还原
例题讲解
已知:
先序:A B D E C
中序:D B E A C
前置口诀(背住)
先序遍历:根 👉 左 👉 右
第一个数 永远是当前整棵树的根
中序遍历:左 👉 根 👉 右
找到根,左边全是左子树,右边全是右子树
第一步:找整棵大树根
先序第一个:A👉 整棵树的根 = A
去中序里找 A:中序:D B E 【A】 C
A 左边:D B E → A 的左子树所有节点
A 右边:C → A 的右子树所有节点
现在结构:
A
/ \
DBE子树 C
第二步:单独处理【左子树:D B E】
左子树节点集合:D B E去先序里,按顺序抠出这三个:先序:A B D E C👉 左子树的先序:B D E
-
左子树的根
先序第一个:B👉 左子树根 = B
去中序找 B:中序左半部分:D 【B】 E
B 左边:D → B 的左孩子
B 右边:E → B 的右孩子
现在结构更新:A / \ B C / \D E
第三步:处理【右子树:C】
右子树只有一个节点 C👉 C 就是 A 的右孩子,叶子节点,没孩子
最终完整二叉树
A
/ \
B C
/ \
D E
第四步:求后序遍历
后序规则:左 → 右 → 根逐个节点走:
D(叶子)
E(叶子)
B(左右走完)
C(叶子)
A(最后根)
✅ 后序结果:D E B C A
5.3 由字符串构建二叉树
通过前序字符串如 "ABD##E#H##CF##G##"(#表示NULL)构建:
这里是牛客网的一道题目牛客网题目
这是他的核心代码
c
BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
if (a[*pi] == '#') {
(*pi)++;
return NULL;
}
BTNode* root = (BTNode*)malloc(sizeof(BTNode));
root->_data = a[*pi];
(*pi)++;
root->_left = BinaryTreeCreate(a, pi);
root->_right = BinaryTreeCreate(a, pi);
return root;
}
这里记得一定要传pi的地址因为如果我们传pi的话,他递归下去不会改变pi的值
六、结点的几个计数问题
6.1 总结点个数 TreeSize
c
int TreeSize(BTNode* root)
{
return root == NULL ? 0 :
TreeSize(root->_left) + TreeSize(root->_right) + 1;
}
思维过程:分而治之
- 空树 → 返回0
- 总结点 = 左子树结点数 + 右子树结点数 + 1(自己)
6.2 叶子结点个数 TreeLeafSize
c
int TreeLeafSize(BTNode* root)
{
if (root == NULL)
return 0;
if (root->_left == NULL && root->_right == NULL)
return 1;
return TreeLeafSize(root->_left) + TreeLeafSize(root->_right);
}
与TreeSize的区别:只有左右都为空才是叶子,不是简单的+1。
6.3 树的高度 TreeHeight
c
int TreeHeight(BTNode* root)
{
if (root == NULL)
return 0;
int leftHeight = TreeHeight(root->_left);
int rightHeight = TreeHeight(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
注意 :不能直接写 max(左, 右) + 1,因为每次递归调用都会重新计算,复杂度会退化。
6.4 第k层结点个数 TreeLevelKSize
c
int TreeLevelKSize(BTNode* root, int k)
{
if (root == NULL)
return 0;
if (k == 1)
return 1;
return TreeLevelKSize(root->_left, k - 1)
+ TreeLevelKSize(root->_right, k - 1);
}
k作为倒计时器:每向下一层k减1,直到k==1说明到达目标层。
七、二叉树的查找 TreeFind
c
BTNode* TreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->_data == x)
return root;
BTNode* ret1 = TreeFind(root->_left, x);
if (ret1)
return ret1; // 短路:左子树找到就不再搜右子树
return TreeFind(root->_right, x);
}
八、层序遍历
见第4.4节。
九、判断是否是完全二叉树 Treecomplete
核心原理
最简短的概述完全二叉树:层序遍历遇到第一个NULL后,**后面所有结点都必须是NULL**。
这里的思路就是层序遍历的一个扩展,我们知道,层序遍历是一层出到带动一层入,判断是是否为完全二叉树就用了这个核心思路,我们让NULL也入队列,这样我们总会出第一个NULL,因为这个也是父亲的儿子节点,所以我们就可以通过他来判断,二叉树是否已经结束了,当出第一个NULL的时候,他就结束了,如果他是完全二叉树,剩下的就全是NULL,如果有不为空的节点他就不是完全二叉树。
代码实现
c
bool Treecomplete(BTNode* root)
{
Queue s;
QueueInit(&s);
Queuepush(&s, root);
// 第一阶段:找到第一个NULL
while (!QueueEmpty(&s))
{
BTNode* ret = Queuefront(&s);
Queuepop(&s);
if (ret == NULL)
break;
Queuepush(&s, ret->_left);
Queuepush(&s, ret->_right);
}
// 第二阶段:检查后面是否全为NULL
while (!QueueEmpty(&s))
{
BTNode* ret2 = Queuefront(&s);
Queuepop(&s);
if (ret2 != NULL)
{
QueueDestory(&s);
return false;
}
}
QueueDestory(&s);
return true;
}
十、二叉树的销毁 TreeDestory
为什么必须用后序遍历?
c
void TreeDestory(BTNode* root)
{
if (root == NULL)
return;
TreeDestory(root->_left); // 1. 先递归销毁左子树
TreeDestory(root->_right); // 2. 再递归销毁右子树
free(root); // 3. 最后释放根结点
}
如果先free根再递归,子树指针就成野指针了!
十一、LeetCode高频OJ题详解
【OJ 1】144. 二叉树的前序遍历
题目:给定二叉树根结点,返回前序遍历的结点值序列。


解题思路:
前序遍历顺序是 根 → 左 → 右。递归实现非常直接:
- 访问根结点
- 递归遍历左子树
- 递归遍历右子树
这里是简单的理解方法,下面我也会为大家写的更详细方便大家理解。
c
代码如下
```c
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int binarytreenode(struct TreeNode* root)
{
return root==NULL?0:binarytreenode(root->left)+binarytreenode(root->right)+1;
}
void prevorder(struct TreeNode* root,int *a,int *i)
{
if(root==NULL)
return;
a[(*i)++]=root->val;
prevorder(root->left,a,i);
prevorder(root->right,a,i);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
*returnSize=binarytreenode(root);
int*a=(int*)malloc(sizeof(int)*(*returnSize));
int i=0;
prevorder(root,a,&i);
return a;
}
这里我见一下前序遍历的详细思路,后面的中序和后续差不多,我就不说了。
一、题目要求(先看懂要干嘛)
给你一棵二叉树,让你返回它的 前序遍历 结果:根 → 左 → 右
题目特别强调:Note: The returned array must be malloced, assume caller calls free().意思:
1.你必须用 malloc 动态开辟数组
2.你不用 free,调用者会自己 free
3.这题就是让我们直接用我们申请的malloc数组,直接存二叉树前序打印的数组,让这个数组输出来的时候就是二叉树前序遍历的形式。
二、整体思路(超级清晰)
整个程序一共做 3 件事:
求树一共有多少节点(用来确定数组开多大)
1.malloc 开出对应大小的数组
2.前序遍历,把值放进数组里
3.把数组返回回去
三、逐行代码讲解
- 求二叉树节点个数
c
int binarytreenode(struct TreeNode* root)
{
return root == NULL ? 0 : binarytreenode(root->left) + binarytreenode(root->right) + 1;
}
作用:
计算这棵树总共有多少个节点因为我们要开数组,必须知道开多大。
逻辑:
如果 root 是 NULL → 返回 0
否则:
左子树节点数 + 右子树节点数 + 自己(1 个)
这就是递归计算节点总数。
- 前序遍历(把值放进数组)
c
void prevorder(struct TreeNode* root, int *a, int *i)
{
if(root == NULL)
return;
a[(*i)++] = root->val; // 根
prevorder(root->left, a, i); // 左
prevorder(root->right, a, i); // 右
}
作用:
前序遍历:根 → 左 → 右把每个节点的值,按顺序放进数组 a 里。
重点解释: a[(*i)++] = root->val先把当前节点值放进数组然后下标 i 自增,准备存下一个数 为什么传 &i?因为要全程共用同一个下标不传地址,i 不会跟着变。
- 主函数(整合所有逻辑)
c
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
// 1. 求节点总数 → 告诉调用者返回数组长度
*returnSize = binarytreenode(root);
// 2. 开数组(必须malloc!题目要求)
int* a = (int*)malloc(sizeof(int)*(*returnSize));
// 3. 遍历下标从0开始
int i = 0;
// 4. 前序遍历,把值放进数组a
prevorder(root, a, &i);
// 5. 返回数组
return a;
}
每一步解释:
1.*returnSize输出型参数把节点总数交给调用者告诉它:返回的数组有多少个元素
2.malloc(...)开出一块刚好能存下所有节点的数组必须用 malloc,不能用局部数组!
3.调用 prevorder递归遍历,把值全部放进数组
return a返回动态开辟的数组
【OJ 2】94. 二叉树的中序遍历
题目:给定二叉树根结点,返回中序遍历的结点值序列。
示例:
输入: [1,2,3,4,5] 对应树结构:
1
/
2
/ \
4 5
输出: [4,2,5,1]
解题思路:
中序遍历顺序是 左 → 根 → 右。递归实现:
- 递归遍历左子树
- 访问根结点
- 递归遍历右子树
代码实现(递归版):
c
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int Treesize(struct TreeNode* root)
{
return root==NULL?0:Treesize(root->left)+Treesize(root->right)+1;
}
void Inorder(struct TreeNode* root,int*a,int*i)
{
if(root==NULL)
return ;
Inorder(root->left,a,i);
a[(*i)++]=root->val;
Inorder(root->right,a,i);
}
int* inorderTraversal(struct TreeNode* root, int* returnSize)
{
* returnSize=Treesize(root);
int*a=(int*)malloc(sizeof(int)*(* returnSize));
int i=0;
Inorder(root,a,&i);
return a;
}
【OJ 3】145. 二叉树的后序遍历
题目:给定二叉树根结点,返回后序遍历的结点值序列。
示例:
输入: [1,2,3,4,5] 对应树结构:
1
/
2
/ \
4 5
输出: [4,5,2,1]
解题思路:
后序遍历顺序是 左 → 右 → 根。递归实现:
c
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int Treesize(struct TreeNode* root)
{
return root==NULL?0:Treesize(root->left)+Treesize(root->right)+1;
}
void postorder(struct TreeNode* root,int *a,int *i)
{
if(root==NULL)
return;
postorder(root->left,a,i);
postorder(root->right,a,i);
a[(*i)++]=root->val;
}
int* postorderTraversal(struct TreeNode* root, int* returnSize) {
* returnSize=Treesize(root);
int*a=(int*)malloc(sizeof(int)*(*returnSize));
int i=0;
postorder(root,a,&i);
return a;
}
【OJ 4】100. 相同的树
题目 :给定两棵二叉树p和q,判断它们是否相同。

示例:
p: 1 q: 1
/ /
2 2
输出:true
解题思路:
两棵树相同 = 根结点相同 + 左子树相同 + 右子树相同
核心:分而治之
相同树的条件(必须同时满足):
1. p和q都是NULL → true
2. p和q只有一个是NULL → false
3. p和q的值不同 → false
4. p的左子树 和 q的左子树 相同
5. p的右子树 和 q的右子树 相同
c
比较 p=[1,2,3] 和 q=[1,2,null]
调用 isSameTree(p=1, q=1)
│
├─ 1==1 且都不是NULL → 继续
│
├─ 比较左子树:isSameTree(p->left=2, q->left=2)
│ │
│ ├─ 2==2 且都不是NULL → 继续
│ │
│ ├─ 比较左子树:isSameTree(p->left->left=NULL, q->left->left=NULL)
│ │ └─ 都为NULL → return true ✓
│ │
│ ├─ 比较右子树:isSameTree(p->left->right=3, q->left->right=NULL)
│ │ └─ 一个NULL一个不是NULL → return false ✗
│ │
│ └─ return false ✗
│
├─ 比较右子树:isSameTree(p->right=3, q->right=NULL)
│ └─ 一个NULL一个不是NULL → return false ✗
│
└─ return false ✗
最终:false
代码实现:
c
bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
// 1. 都是空 → 相同
if (p == NULL && q == NULL)
return true;
// 2. 只有一个是空 → 不同
if (p == NULL || q == NULL)
return false;
// 3. 值不同 → 不同
if (p->val != q->val)
return false;
// 4. 递归比较左右子树
return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
易错点:
- 不能写成
if (p == NULL && q == NULL) return true后直接return true - 必须继续递归比较左右子树!
【OJ 5】572. 另一棵树的子树
题目 :给定树subRoot,判断它是否是树root的子树。

示例:
root: 3 subRoot: 3
/ \ /
4 5 4
/ \
1 2
输出:true(subRoot的子树与root的左子树相同)
解题思路:
核心思想:把问题分解为三个子问题
判断 subRoot 是否是 root 的子树,等价于:
1. root 和 subRoot 本身相同? → isSameTree(root, subRoot)
2. subRoot 是 root 左子树的子树? → isSubtree(root->left, subRoot)
3. subRoot 是 root 右子树的子树? → isSubtree(root->right, subRoot)
代码实现:
c
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
if(p==NULL&&q==NULL)
return true;
if(p==NULL||q==NULL)
return false;
if(p->val!=q->val)
return false;
return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot)
{
if(root==NULL)
return false;
if(root->val==subRoot->val&&isSameTree(root,subRoot))
return true;
return isSubtree(root->left,subRoot)||isSubtree(root->right,subRoot);
}
子树 vs 子树:
- 子树:一个树的所有结点都是另一个树中某个结点的后代
- 子结构:只要结构相同就行,不要求是完整的树
【OJ 6】965. 单值二叉树
题目 :判断一棵二叉树是否是单值二叉树(所有结点值都相同)。

示例:
输入: [1,1,1,1,1,null,1] 对应树结构:
1
/ \
1 1
/ \ \
1 1 1
输出:true
解题思路:
方法:递归比较
-
如果当前结点值和root->val不同 → false
-
递归检查左右子树是否也都是单值
单值二叉树的条件:
- 当前结点值 == root->val
- 左子树所有结点值 == root->val
- 右子树所有结点值 == root->val
终止条件:
- root == NULL → return true
- root->val != f → return false
`
代码如下
c
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
bool InOrder(struct TreeNode* root,int f)
{
if (root == NULL)
{
return true;
}
else if(root->val!=f)
{
return false;
}
return InOrder(root->left,f)&&InOrder(root->right,f);
}
bool isUnivalTree(struct TreeNode* root)
{
if(root==NULL)
return true;
return InOrder(root,root->val);
}
十二、课后习题
12.1 二叉树性质选择题
【题目1】 某二叉树共有399个结点,其中有199个度为2的结点,则该二叉树中的叶子结点数为( B )
A. 不存在这样的二叉树 B. 200 C. 198 D. 199
解析:n₀ = n₂ + 1 = 199 + 1 = 200
【题目2】 下列数据结构中,不适合采用顺序存储结构的是( A )
A. 非完全二叉树 B. 堆 C. 队列 D. 栈
解析:非完全二叉树用数组存储会有大量空间浪费。
【题目3】 在具有2n个结点的完全二叉树中,叶子结点个数为( A )
A. n B. n+1 C. n-1 D. n/2
解析:设n₀=n,n₁=0或1,n₀+n₁+n₂=2n,n₀=n₂+1 → n₀=n。
【题目4】 一棵完全二叉树的结点数为531个,则这棵树的高度为( B )
A. 11 B. 10 C. 8 D. 12
解析:2^9=512 ≤ 531 < 2^10=1024,所以h=10。
【题目5】 一个具有767个结点的完全二叉树,其叶子结点个数为( B )
A. 383 B. 384 C. 385 D. 386
解析:767是奇数→n₁=0→n₀=(767+1)/2=384。
12.2 遍历还原选择题
【题目1】 某完全二叉树按层次输出序列为ABCDEFGH,其前序序列为( A )
A. ABDHECFG B. ABCDEFGH C. HDBEAFCG D. HDEBFGCA
【题目2】 二叉树先序遍历:EFHIGJK;中序遍历:HFIEJKG。则二叉树根结点为( A )
A. E B. F C. G D. H
解析:先序第一个元素是根 → E。
【题目3】 中序遍历:badce,后序遍历:bdeca,则前序遍历序列为( D )
A. adbce B. decab C. debac D. abcde
【题目4】 后序遍历与中序遍历相同,均为ABCDEF,则按层次输出的序列为( D )
A. FEDCBA B. CBAFED C. DEFCBA D. ABCDEF
解析:每个结点只有右子树,没有左子树,层序输出即从根开始 → ABCDEF。
好了,本期二叉树详解就到这里。如果对你有帮助,还请一键三连支持,我们下期再见!