【数据结构】二叉树详解:全代码逐行解析+6道LeetCode高频OJ题图解

【数据结构】二叉树详解:从基础概念到高频考点全覆盖(全代码逐行解析)

大家好,今天我们来系统学习二叉树这一重要数据结构。

二叉树是计算机科学中最核心的概念之一,无论是后续学习算法(如搜索树、平衡树、红黑树),还是应对考研/求职面试,都是必须掌握的内容。本文将带你从零开始,系统理解二叉树的基本概念链式实现核心操作 以及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结论的应用

我们可以通过下列题目巩固一下

  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

  1. 左子树的根
    先序第一个: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. 二叉树的前序遍历

题目:给定二叉树根结点,返回前序遍历的结点值序列。

解题思路

前序遍历顺序是 根 → 左 → 右。递归实现非常直接:

  1. 访问根结点
  2. 递归遍历左子树
  3. 递归遍历右子树
    这里是简单的理解方法,下面我也会为大家写的更详细方便大家理解。
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.把数组返回回去

三、逐行代码讲解

  1. 求二叉树节点个数
c 复制代码
int binarytreenode(struct TreeNode* root)
{
    return root == NULL ? 0 : binarytreenode(root->left) + binarytreenode(root->right) + 1;
}

作用:

计算这棵树总共有多少个节点因为我们要开数组,必须知道开多大。

逻辑:

如果 root 是 NULL → 返回 0

否则:

左子树节点数 + 右子树节点数 + 自己(1 个)

这就是递归计算节点总数。

  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 不会跟着变。

  1. 主函数(整合所有逻辑)
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]

解题思路

中序遍历顺序是 左 → 根 → 右。递归实现:

  1. 递归遍历左子树
  2. 访问根结点
  3. 递归遍历右子树

代码实现(递归版)

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

  • 递归检查左右子树是否也都是单值

    单值二叉树的条件:

    1. 当前结点值 == root->val
    2. 左子树所有结点值 == root->val
    3. 右子树所有结点值 == 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。


好了,本期二叉树详解就到这里。如果对你有帮助,还请一键三连支持,我们下期再见!

相关推荐
田梓燊2 小时前
翻转二叉树
leetcode
搬砖的小码农_Sky2 小时前
比特币区块链的算法架构
算法·架构·去中心化·区块链
流年如夢2 小时前
顺序表(LeetCode)
c语言·数据结构·leetcode·职场和发展
say_fall2 小时前
校招必看:八大排序算法原理、复杂度与高频面试题
数据结构·c++·算法·排序算法
贾斯汀玛尔斯11 小时前
每天学一个算法--LSM-Tree(Log-Structured Merge Tree)
java·算法·lsm-tree
浅念-15 小时前
刷穿LeetCode:BFS 解决 Flood Fill 算法
数据结构·c++·算法·leetcode·职场和发展·bfs·宽度优先
做cv的小昊15 小时前
【TJU】研究生应用统计学课程笔记(8)——第四章 线性模型(4.1 一元线性回归分析)
笔记·线性代数·算法·数学建模·回归·线性回归·概率论
贾斯汀玛尔斯16 小时前
每天学一个算法--倒排索引(Inverted Index)
算法·inverted-index