快速复习之数据结构篇——二叉树(三)

文章目录

◆ 博主名称:此生决int

大家好,欢迎来到我的博客~

⭐ 个人专栏:快速复习系列

⭐ 热门专栏:算法基础到精通系列

上期回顾

上期我们主要介绍了二叉树的顺式存储方式,堆的概念和性质,以及应用,重点介绍了向上调整算法和向下调整算法,以及堆排序

文章概要

本文系统讲解二叉树链式结构的核心知识点与高频考点,从入门到进阶全覆盖:
✅ 基础必学:前序 / 中序 / 后序遍历、层序 BFS 遍历,彻底搞懂二叉树遍历逻辑
✅ 核心算法:节点总数 / 叶子节点数 / 树高度(含优化)/ 第 k 层节点数 / 查找指定节点,附递归实现与优化思路
✅ 高频 OJ 题:单值二叉树、相同的树、对称二叉树、二叉树创建、前序遍历 OJ 题,拆解解题思路
✅ 配套练习:二叉树相关选择题,巩固基础知识点
帮你告别二叉树递归 bug,轻松应对期末、竞赛与面试!

二叉树链式结构的实现

1 前置说明

现在大家对二叉树结构掌握还不够深入,此处手动快速创建一棵简单的二叉树,

cpp 复制代码
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
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;
}

我们先来回顾下二叉树的概念,二叉树是:

  1. 空树
  2. 非空:根结点,根结点的左子树、根结点的右子树组成的。
    说白了,树就是由根节点,左树+右树。左树又由根节点+左树+右树
    所以,二叉树定义是递归式

2.二叉树的遍历⭐️⭐️

遍历是基础中的基础!!!所谓二叉树遍历是按照某种特定的规则,依次对二叉树中的结点进行相应的操作,并且每个结点只操作一次 。前,中,后遍历的区别就是根节点访问的先后顺序!!又称为先根遍历、中根遍历和后根遍历。

前序遍历

访问顺序:根--->左--->右

cpp 复制代码
void preorder(treenode*root)
{
    if(root==NULL)
    return ;
    //访问根节点
    //例如:cout<<root->val<<endl;
    cout<<root->val<<endl;
    //访问左子树
    preorder(root->left);
    //访问右子树
    preorder(root->right);
}
中序遍历

访问顺序:左--->根--->右

cpp 复制代码
void inorder(treenode*root)
{
    if(root==NULL)
    return ;
    //访问左子树
    inorder(root->left);
    //访问根节点
    cout<<root->val<<endl;
    //访问右子树
    inorder(root->right);
}
后序遍历

访问顺序:左--->右--->根

cpp 复制代码
void postorder(treenode*root)
{
    if(root==NULL)
    return ;
    //访问左子树
    postorder(root->left);
    //访问右子树
    postorder(root->right);
    //访问根节点
    cout<<root->val<<endl;
}

3.二叉树相关选择题

1.某完全二叉树按层次输出(同一层从左到右)的序列为 ABCDEFGH 。该完全二叉树的前序序列为( )

A ABDHECFG

B ABCDEFGH

C HDBEAFCG

D HDEBFGCA

2.二叉树的先序遍历和中序遍历如下:先序遍历:EFHIGJK;中序遍历:HFIEJKG.则二叉树根结点为()

A E

B F

C G

D H

3.设一课二叉树的中序遍历序列:badce,后序遍历序列:bdeca,则二叉树前序遍历序列为____。

A adbce

B decab

C debac

D abcde

4.某二叉树的后序遍历序列与中序遍历序列相同,均为 ABCDEF ,则按层次输出(同一层从左到右)的序列为

A FEDCBA

B CBAFED

C DEFCBA

D ABCDEF

答案:1,A 2,A 2,D 4,A

4.二叉树的链式题目⭐️⭐️⭐️

二叉树结点个数

int BinaryTreeSize(BTNode* root);

cpp 复制代码
int TreeSize(BTNode* root)
{
    return root == NULL ? 0 :
        TreeSize(root->left) + TreeSize(root->right) + 1;
}

二叉树叶子结点个数

int BinaryTreeLeafSize(BTNode* root);

cpp 复制代码
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);
}

二叉树的高度

cpp 复制代码
int TreeHeight(BTNode* root)
{
    if (root == NULL)
        return 0;

    return TreeHeight(root->left) > TreeHeight(root->right) ?
        TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;
}
优化⭐️⭐️
cpp 复制代码
int TreeHeight(BTNode* root)
{
    if(!root) return 0;
    int lh = TreeHeight(root->left);
    int rh = TreeHeight(root->right);
    return (lh > rh ? lh : rh) + 1;
}

二叉树第k层结点个数

int BinaryTreeLevelKSize(BTNode* root, int k);

cpp 复制代码
// 二叉树第k层节点个数
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);
}

二叉树查找值为x的结点

BTNode* BinaryTreeFind(BTNode* root, BTDataType x);

cpp 复制代码
// 二叉树查找值为x的节点
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;

    BTNode* ret2 = TreeFind(root->right, x);
    if (ret2)
        return ret2;

    return NULL;
}

5.二叉树相关算法题⭐️⭐️⭐️

单值二叉树

单值二叉树

cpp 复制代码
/**
 * 解题思路:
 * 单值二叉树定义为所有节点值完全相同的二叉树。
 * 本题采用递归方式自顶向下判断:
 * 1. 空树或只有一个节点(叶子节点)直接视为单值二叉树。
 * 2. 检查当前节点与左右子节点的值是否相等(若子节点存在)。
 * 3. 若任一直接子节点的值与当前节点不同,则立即返回 false。
 * 4. 若当前层满足条件,则递归检查左右子树是否也都是单值二叉树。
 * 5. 整个树满足单值当且仅当所有层次都通过上述检查。
 */
class Solution {
public:
    bool isUnivalTree(TreeNode* root) {
        // 空树认为是单值二叉树
        if (root == NULL)
            return true;
        // 叶子节点:没有左右孩子,已经满足单值要求
        if (root->left == NULL && root->right == NULL)
            return true;

        // 检查左孩子是否存在且值是否相等
        if (root->left != NULL && root->left->val != root->val)
            return false;
        // 检查右孩子是否存在且值是否相等
        if (root->right != NULL && root->right->val != root->val)
            return false;

        // 当前节点与孩子值一致,继续递归检查左右子树是否也是单值二叉树
        return isUnivalTree(root->left) && isUnivalTree(root->right);
    }
};

相同的树

相同的树

cpp 复制代码
/**
 * 解题思路:
 * 判断两棵二叉树是否相同,需要同时满足结构相同和节点值相同。
 * 采用递归方式同步遍历两棵树:
 * 1. 如果两个节点都为空,说明当前分支结构一致且无值差异,返回 true。
 * 2. 如果只有一个为空,说明结构不同,返回 false。
 * 3. 如果两个节点都不为空,先判断当前节点值是否相等:
 *    - 不相等则直接返回 false。
 *    - 相等则继续递归检查左子树和右子树是否分别相同。
 * 4. 左右子树均相同时,两棵树才完全相同。
 */

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
    // 两个节点都为空,当前分支结构相同,返回 true
    if (p == NULL && q == NULL)
        return true;
    // 只有一个为空,结构不同,返回 false
    if (p == NULL || q == NULL)
        return false;
    
    // 当前节点值相等时,继续递归比较左右子树
    if (p->val == q->val) {
        // 左右子树必须同时相同,才返回 true
        if ((isSameTree(p->left, q->left)) && (isSameTree(p->right, q->right)))
            return true;
        else
            return false;
    }
    // 节点值不相等,直接返回 false
    else
        return false;
}

对称二叉树

对称二叉树

cpp 复制代码
/**
 * 解题思路:
 * 轴对称二叉树要求树的左子树与右子树互为镜像。
 * 通过递归辅助函数同步比较两棵树是否镜像对称:
 * 1. 两树都为空:对称,返回 true。
 * 2. 一空一非空:不对称,返回 false。
 * 3. 根节点值不等:不对称,返回 false。
 * 4. 当前节点值相等时,进一步检查:
 *    - 左树的左孩子 与 右树的右孩子 是否镜像对称
 *    - 左树的右孩子 与 右树的左孩子 是否镜像对称
 * 5. 二者皆对称时,说明两树整体镜像对称。
 * 主函数判断 root 为空(对称)或调用辅助函数比较左右子树。
 */

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

// 辅助函数:判断 ret1 与 ret2 是否镜像对称
bool _isSymmetric(struct TreeNode *ret1, struct TreeNode* ret2) {
    // 两个节点都为空,对称
    if (ret1 == NULL && ret2 == NULL)
        return true;
    // 只有一个为空,不对称
    if (ret1 == NULL || ret2 == NULL)
        return false;
    // 节点值不等,不对称
    if (ret1->val != ret2->val)
        return false;
    // 当前层对称,递归比较"错位"的子树
    return (_isSymmetric(ret1->left, ret2->right) &&
            _isSymmetric(ret1->right, ret2->left));
}

// 主函数:判断整棵树是否轴对称
bool isSymmetric(struct TreeNode* root) {
    // 空树视为轴对称
    if (root == NULL)
        return true;
    // 比较左子树和右子树是否镜像对称
    return _isSymmetric(root->left, root->right);
}

另一棵树的子树
另一棵树的子树

cpp 复制代码
/**
 * 解题思路:
 * 要判断 subRoot 是否是 root 的子树,可以分解为两个步骤:
 * 1. 判断两棵树是否完全相同(结构和节点值均一致)。
 * 2. 遍历 root 树的每一个节点,将以该节点为根的子树与 subRoot 进行"相同性"比较。
 *
 * 具体做法:
 * - 利用辅助函数 isSameTree 判断两棵树是否完全一致。
 * - 在 isSubtree 中,先比较当前 root 与 subRoot 是否相同,若相同则直接返回 true。
 * - 否则,如果 root 的左子树存在,递归地在左子树中查找 subRoot;
 *   如果找到则返回 true。
 * - 同理,如果 root 的右子树存在,递归地在右子树中查找 subRoot;
 *   如果找到则返回 true。
 * - 如果当前节点、左子树、右子树中都没有找到相同的子树,则返回 false。
 *
 * 注意:本代码假定 subRoot 非空。若 root 为空且 subRoot 非空,最终会因没有左右子树而返回 false。
 */

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

// 判断两棵树 p 和 q 是否完全相同
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) {
        if ((isSameTree(p->left, q->left)) && (isSameTree(p->right, q->right)))
            return true;
        else
            return false;
    }
    // 节点值不相等,树不相同
    else
        return false;
}

// 判断 subRoot 是否是 root 的子树
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot) {
    // 先检查以 root 为根的树是否与 subRoot 完全相同
    if (isSameTree(root, subRoot))
        return true;
    // 否则在左子树中递归寻找
    if (root->left) {
        if (isSubtree(root->left, subRoot))
            return true;
    }
    // 否则在右子树中递归寻找
    if (root->right) {
        if (isSubtree(root->right, subRoot))
            return true;
    }
    // 当前 root 及其左右子树均未找到,返回 false
    return false;
}

二叉树的创建⭐️⭐️⭐️⭐️

二叉树的创建

cpp 复制代码
/**
 * 解题思路:
 * 题目要求根据给定的先序遍历字符串('#' 表示空节点)建立二叉树,
 * 并对该二叉树进行中序遍历输出结果。
 *
 * 整体分为三步:
 * 1. 定义二叉树节点结构体:存储字符值、左孩子指针、右孩子指针。
 * 2. 利用递归构建二叉树:
 *    - 用一个索引指针遍历字符串。
 *    - 如果当前字符为 '#',表示空树,索引后移并返回 NULL。
 *    - 否则创建新节点,字符赋值,索引后移;
 *      然后递归构建左子树,再递归构建右子树。
 * 3. 对构建好的二叉树进行中序遍历(左-根-右),打印节点值。
 */

#include <iostream>
using namespace std;

// 二叉树节点定义
struct TreeNode {
    struct TreeNode* left;
    struct TreeNode* right;
    char val;          // 节点存储的字符值
};

/**
 * 根据先序字符串递归创建二叉树
 * @param s   输入的先序遍历字符串
 * @param pi  当前处理字符的索引指针(通过指针传递,实现跨递归修改)
 * @return    新生成的二叉树节点的指针
 */
TreeNode* CreatTree(string& s, int *pi) {
    // 遇到 '#' 表示空节点,索引前进并返回空指针
    if (s[*pi] == '#') {
        (*pi)++;
        return NULL;
    }
    // 分配新节点
    // 使用 malloc 分配内存(也可用 new)
    TreeNode* ret = (TreeNode*)malloc(sizeof(TreeNode));
    // 节点赋值当前字符,然后索引前进
    ret->val = s[*pi];
    (*pi)++;
    // 递归构建左子树和右子树
    ret->left  = CreatTree(s, pi);
    ret->right = CreatTree(s, pi);
    return ret;
}

/**
 * 中序遍历二叉树(左 - 根 - 右)
 * @param root 当前树的根节点
 */
void Mind(TreeNode* root) {
    if (root == NULL)
        return;
    // 遍历左子树
    Mind(root->left);
    // 打印当前节点值(使用 printf 保持原风格)
    printf("%c ", root->val);
    // 遍历右子树
    Mind(root->right);
}

int main() {
    string s;
    cin >> s;                  // 读入先序遍历字符串,例如:ABC##DE#G##F###
    int i = 0;                 // 初始化字符串索引

    TreeNode* root = CreatTree(s, &i); // 构建二叉树
    Mind(root);                // 中序遍历并输出结果

    return 0;
}
// 64 位输出建议使用 printf("%lld"),本处输出字符未涉及此项

二叉树的前序遍历OJ

前序

cpp 复制代码
/**
 * 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)
 {
    if(root==NULL)
     return  0;
    return (TreeSize(root->left)+TreeSize(root->right)+1);
 }
 void PreOrder(struct TreeNode* root,int *arr,int *pi)
 {
    if(root==NULL)
    return;
    arr[(*pi)++]=root->val;
    PreOrder(root->left,arr,pi);
    PreOrder(root->right,arr,pi);

 }
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
    //if(root==NULL)
   // return NULL;
    *returnSize=TreeSize(root);
    
    int *arr=(int *)realloc(arr,sizeof(int)*(*returnSize));
     int i=0;
PreOrder(root,arr,&i);
return arr;
}

注意事项:

  • leetcode返回数组要返回数组的大小,用的返回型参数
  • 递归传的参要用pi,这样 i 才能往后加(类似计算数的节点个数那个题)

二叉树的层序遍历(BFS)

cpp 复制代码
void TreeLevelOrder(BTNode* root)
{
    Queue 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);
    }

    QueueDestroy(&q);
}

下期预告

OK,今天的博客先分享到这里,我们上一篇博客学习了堆排序,想不想学习更多的排序算法?下一期我们将一起学习数据结构的最后一节------七大排序算法!!!

结语

**  本文到此结束,欢迎各位在评论区探讨交流。需要快速复盘、精简学习内容的朋友,别忘了订阅我的快速复习专栏哦,主打专注整合学习中的精华,我们一起加油吧!**

相关推荐
Liangwei Lin1 小时前
LeetCode 78. 子集
数据结构·算法·leetcode
khalil10202 小时前
代码随想录算法训练营Day-48 单调栈02 | 42. 接雨水、84.柱状图中最大的矩形
数据结构·c++·算法·leetcode·单调栈·接雨水
大大杰哥2 小时前
Java集合框架(List/Set/Queue)核心总结与代码示例
java·数据结构
多加点辣也没关系2 小时前
数据结构与算法总章
数据结构·算法
hnjzsyjyj2 小时前
洛谷 P1305:新二叉树 ← DFS
数据结构·dfs
如君愿3 小时前
考研复习 Day 34 | 习题--计算机网络 第六章(应用层 下)、数据结构 查找算法(下)
数据结构·计算机网络·考研·课后习题
Languorous.3 小时前
数据结构初阶|二叉树入门,从零到一吃透基础
数据结构
凯瑟琳.奥古斯特3 小时前
丑数II C++三指针解法(力扣264)
数据结构·c++·算法·leetcode·职场和发展
郝学胜-神的一滴3 小时前
二叉树与递归:解锁高级数据结构的编程内功心法
开发语言·数据结构·c++·算法·面试