数据结构刷题训练——二叉树篇(一)

news2024/12/29 9:35:00

📙作者简介: 清水加冰,目前大二在读,正在学习C/C++、Python、操作系统、数据库等。

📘相关专栏:C语言初阶、C语言进阶、C语言刷题训练营、数据结构刷题训练营、有感兴趣的可以看一看。

欢迎点赞 👍 收藏 ⭐留言 📝 如有错误还望各路大佬指正!

✨每一次努力都是一种收获,每一次坚持都是一种成长✨       

在这里插入图片描述

目录

 前言

📖题目1:翻转二叉树

📖题目2:相同的树

📖题目3:对称二叉树

📖题目4:另一棵树的子树

 📖题目5:二叉树的前序遍历

总结


 前言

        我们学习了二叉树的顺序结构和链式结构,在日常刷题在,我们最常见的就是链式二叉树,刚学习完链式二叉树刷题上手比较难,本期我将继续开始数据结构刷题专栏,为大家提供二叉树(初阶)相关的练习和力扣OJ的经典题目以及题目讲解,以便于大家更容易上手二叉树部分的刷题。


📖题目1:翻转二叉树

题目描述:

题目链接:

翻转二叉树icon-default.png?t=N7T8https://leetcode.cn/problems/invert-binary-tree/description/

✨题目解析:

         本道题目较为简单,要学会巧妙运用递归解决问题,翻转二叉树也就是翻转它的左子树和右子树,然后不断的向下分,在使用递归时要注意递归结束的条件,确保所有的控件路径都返回值。

 代码如下:

struct TreeNode* invertTree(struct TreeNode* root){
    if(root==NULL)
    {
        return NULL;
    }
    struct TreeNode* left=invertTree(root->left);
    struct TreeNode* right=invertTree(root->right);  
    root->right=left;
    root->left=right;
    return root;
}

📖题目2:相同的树

题目描述:

 

 题目链接:

相同的树icon-default.png?t=N7T8https://leetcode.cn/problems/same-tree/description/

✨题目解析:

        这道题目的递归思路比上道题复杂一点,主要考察的是对递归的使用以及控制。题目中传进来的是两个树,那要如何去控制递归呢?

两棵树是否相同,主要就是比较对于位置的节点数据是否相同,如果两棵树的每个对应节点都相等,这两棵树就相等,但单纯的判断值是否相等无法做到所有的控件路径都返回值,这里也要考虑特殊情况,当一颗树为NULL,另一颗树不为空,这时就要返回false,两棵树当前节点都为NULL,就返回true。有了这些前提,我们对代码进行实现,那这样写对不对呢?

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    if(p==NULL&&q==NULL)//两个都为NULL就返回True
    {
        return true;
    }
    if(p==NULL||q==NULL)//两棵树都不为NULL为前提
    {
        return false;
    }
    if(p->val==q->val)
    {
        return true;
    }
    return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
}

 这样写乍一看没什么问题,但它在力扣上通过不了,例如:

p:[1,2]        q:[1,null,2]

 你会发现它根本就没递归进去就返回了,只比较了根节点的值。我们需要让它递归到最后,也就是叶子节点,从叶子节点开始。既然正向行不通,那就逆向,如果它们的val不相等就返回false。

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);
}

这样就可以递归到叶子节点,如果递归到最后一直到叶子节点都没有返回false,那就说明递归路径上的节点都相等,如果有一个返回的是false,最后返回的是左子树与右子树取&&的结果,最终一定会返回false。如果节点都相当才会返回true。

例如这棵树:递归从左子树开始,节点2的left递归返回true,right返回也是true,那节点2整体返回就是true,然后开始递归右子树,右子树返回和左子树一样也是true。然后返回到节点1的递归左右子树都为true,那整体就返回true。

📖题目3:对称二叉树

题目描述:

 

 题目链接:

对称二叉树icon-default.png?t=N7T8https://leetcode.cn/problems/symmetric-tree/✨题目解析:

        这道题的解法和上道题很像,也就是在上道题做了一点进阶。

它让我们判断对称,那我们就可以将这棵树的左子树和右子树分为两棵树,上道题目判断是相同位置的节点相同,我们传的都是p->left,q->left,我们观察一下对称的二叉树,左子树的左孩子节点与右子树的右孩子节点相同,左子树的右孩子与右子树的左孩子相同。

 那我们在判断相同时是否就可以这样传值,去比较左子树的左孩子和右子树的右孩子,左子树的右孩子和右子树的左孩子是否相同。

 那我们就只需改变一下传的参数即可。我们套用一下上边相同的二叉树的进行变形一下:

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->right)&&isSameTree(p->right,q->left);
}
bool isSymmetric(struct TreeNode* root){
    return isSameTree(root->left,root->right);
}

📖题目4:另一棵树的子树

 

 题目链接:

另一棵树的子树icon-default.png?t=N7T8https://leetcode.cn/problems/subtree-of-another-tree/description/

✨题目解析:

判断一颗树是否是另一棵树的子树,还是会用到两棵树是否相等。只不过这道题目需要注意一下开始比较的条件。我们先写一个框架

bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){

    if(root==NULL)
    {
        return false;
    }

    if(root->val==subRoot->val)
    {
        
        //开始比较
        ……

    }

    return isSubtree(root->left,subRoot)||isSubtree(root->right,subRoot);
            //遍历左子树                      // 遍历右子树
}

 

         如果完整的树为NULL那就不在需要判断了,直接返回false。然后开始比较root的值是否与subRoot的值相等,如果相等就从值相等的节点位置开始判断两棵树是否相同。在寻找相同值的过程中我们需要遍历二叉树,遍历二叉树最简便的方法就是使用递归,只要root左子树或右子树有一个存在子树相同,那就返回true(所以这里使用 “ || ” )。 

注意:只有返回类型为bool类型时才可以进行&&或||的操作

完整代码如下: 

bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
    if(root==NULL)
    {
        return false;
    }
    if(root->val==subRoot->val)
    {
        if(isSameTree(root,subRoot))    //isSameTree函数使用的是上述题目2的代码
        {
            return true;
        }
    }
    return isSubtree(root->left,subRoot)||isSubtree(root->right,subRoot);
}

 📖题目5:二叉树的前序遍历

 题目描述:

 

题目链接:

二叉树的前序遍历icon-default.png?t=N7T8https://leetcode.cn/problems/binary-tree-preorder-traversal/

✨题目解析:

        这道二叉树的前序遍历和我们·之前的不太一样,我们需要把遍历的值放在一个数组中,所以首先我们还需要知道二叉树的节点个数才可以创建数组,然后再开始遍历二叉树。

 我们可以分两个接口来写:

int* preorderTraversal(struct TreeNode* root, int* returnSize){

    *returnSize=0;    //返回的数组大小

    int n=TreeSize(root);

    int* ret=(int*)malloc(sizeof(int)*n);

    preorder(root,ret,returnSize);//遍历二叉树

    return ret;//返回数组
}

这里的前序遍历和之前没什么区别,区别就在原本输出的位置需要把数据存到数组中:

void preorder(struct TreeNode* root,int* arr,int* i)
{
    if(root==NULL)
    {
        return;
    }
    //前序遍历的顺序:根、左子树、右子树
    arr[(*i)++]=root->val;
    preorder(root->left,arr,i);    
    preorder(root->right,arr,i);
}

完整代码如下:

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* i)
{
    if(root==NULL)
    {
        return;
    }
    arr[(*i)++]=root->val;
    preorder(root->left,arr,i);    
    preorder(root->right,arr,i);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize){
    *returnSize=0;
    int n=TreeSize(root);
    int* ret=(int*)malloc(sizeof(int)*n);
    preorder(root,ret,returnSize);
    return ret;
}

 此外还有中序遍历、后序遍历、大家可以自行练习。

二叉树的中序遍历

二叉树的后序遍历 

 


总结

        本期主要的内容是学会二叉树的递归遍历,除此之外二叉树还有层序遍历,层序遍历需要的数据结构更复杂一点,下期我再向大家一一介绍,以上便是本前期全部内容,最后,感谢阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1063641.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学习记忆——方法篇——整除特点

理解记忆法 对于数的整除特征大家都比较熟悉:比如4看后两位(因为100是4的倍数),8看后三位(因为1000是8的倍数),5末尾是0或5,3与9看各位数字和等等,今天重点研究一下3,9,…

创新家庭办公室:打造完美工作空间的秘诀

一个精心策划的家庭办公室有很多好处,何不把临时工作区升级改造为你的专属工作区呢,还能为这些至关重要的区域注入新的活力。 创造多用途的起居室:我们大多数人都不曾拥有一个可以完全根据工作需求设计的独立家庭办公室——所以有时候要找到…

目标检测算法改进系列之Backbone替换为RIFormer

RIFormer简介 Token Mixer是ViT骨干非常重要的组成成分,它用于对不同空域位置信息进行自适应聚合,但常规的自注意力往往存在高计算复杂度与高延迟问题。而直接移除Token Mixer又会导致不完备的结构先验,进而导致严重的性能下降。 原文地址&…

Pytorch之shuffleNet图像分类

💂 个人主页:风间琉璃🤟 版权: 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主💬 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 前言 ShuffleNet是Face(旷视)在2017年发布的一个高效率…

【傅里叶梅林图像配准】用于图像配准的傅里叶梅林相位相关性的实现(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

Python之元组

Python之元组 元组tuple 一个有序的元素组成的集合使用小括号 ( ) 表示元组是不可变对象 tuple(), (), type(()) # 空元组 ((), (), tuple)(1,), (1) # 元组中只有1必须加逗号,否则就是1了 # ((1,), 1)x 1, 2 # 以逗号分隔的内容会形成元组,封装元组x…

壁炉装饰:突破传统的创新趋势

壁炉,一直以来都是家庭温馨的象征,但它也是家居装饰中一个充满潜力的元素。如今,随着设计趋势的不断演变,壁炉装饰已经迈入了一个全新的时代,融合了美学、功能和可持续性,为家庭创造了更多可能性。 壁炉装饰…

强化学习环境 - robogym - 学习 - 3

强化学习环境 - robogym - 学习 - 3 文章目录 强化学习环境 - robogym - 学习 - 3项目地址为什么选择 robogymObservation - 观测信息Action - 动作信息Initialization - 初始状态设置 项目地址 https://github.com/openai/robogym 为什么选择 robogym 自己的项目需要做一些机…

04.数据解析之css选择器

1、常见数据类型 结构化的数据是指可以使用关系型数据库表示和存储,表现为二维形式的数据。一般特点是:数据以行为单位,一行数据表示一个实体的信息,每一行数据的属性是相同的。 1、1 结构化数据 ​ 结构化的数据是指可以使用关…

策略模式与模板方法结合案例

一、背景 上周在迁移项目MQ工程的时候,重新Review代码,发现有一段代码综合使用了策略模式和模板方法,下面讲解一下具体场景应用的思路。 二、模板方法 策略模式前段时间有一个关于库存具体案例,详见 库存管理与策略模式。 模板…

智能银行卡明细筛选与统计,轻松掌握账户总花销!

作为现代生活的重要组成部分,银行卡成为了我们日常消费和收入的主要途径。但是,当我们需要了解自己的银行卡账户的总花销时,繁琐的明细筛选和统计工作常常让人头疼。现在,让我们向您推荐一款智能银行卡明细筛选与统计工具&#xf…

基于SpringBoot的学生选课系统

基于SpringBoot的学生选课系统的设计与实现,前后端分离 开发语言:Java数据库:MySQL技术:SpringBootMyBatisVue工具:IDEA/Ecilpse、Navicat、Maven 前台主页 登录界面 管理员界面 教师界面 学生界面 摘要 学生选课系统…

halcon 数字识别

文章目录 素材交互选取区域阈值分割特征提取识别字符显示全部代码 素材 dev_get_window(WindowHandle) **读取图像 read_image(Image,C:/Users/Augustine/Desktop/1.png) **把图像转正,镜像方式 mirror_image(Image,ImageMirror,row) mirror_image(ImageMirror,Imag…

Python 无废话-基础知识面向对象编程详解

类定义 如何理解万物皆对象? 生活中一些事物,动物(可爱的小狗、调皮的小猫)、交通工具(比亚迪U8汽车、飞机)、人(学生、教师)…… 这些对象都有着独特或共性的属性和方法来描述其…

Android:实现Camera前后双摄

效果展示 一.概述 本博文讲解如何实现手机前后两颗摄像头同时预览并显示 我之前博文《OpenGLES:GLSurfaceView实现Android Camera预览》对单颗摄像头预览做过详细讲解,而前后双摄实现原理其实也并不复杂,粗糙点说就是把单摄像头预览流程写两…

Pytorch之MobileNetV3图像分类

💂 个人主页:风间琉璃🤟 版权: 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主💬 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 前言 由于传统卷积神经网络, 内存需求大、 运算量大导致无法在移动…

多线程JUC

文章目录 多线程一.什么是多线程二.多线程的两个概念三.线程的实现方式四.常见的成员方法五.线程安全的问题六.死锁七.生产者和消费者 多线程 一.什么是多线程 进程:是程序的基本执行实体 理解:每一个运行的软件就是一个进程 线程:是操做系统能够进行运算调度的最小单位,它…

Halcon 从基础到精通-02- 开发基于HALCON的应用

HALCON的应用通过HDevelop应用来构建原型。HDevelop的开发主要有3种形式。 Start from Scratch: 手动通过脚本,把HDevelop的代码转化为一般的编程语言。如,上一节提到,其实,每个operators,也许并不一样,需要依据HALC…

开发工具箱 —— it-tools

文章目录 开发工具箱 —— it-tools安装访问效果 开发工具箱 —— it-tools 安装 docker 安装教程:在 CentOs7 中安装宝塔面板和 Docker(包括MySQL,Redis) docker 安装命令 docker run -d --name it-tools --restart unless-st…

[unity]保存文件的路径设置

序 比如,序列化了一个数组,保存到磁盘上。 原来的路径是"D://test.bin",能跑,但是有点问题:序列化出来的文件和原项目离的太远,不好管理。 要是能保存到unity工程的文件夹里就好了。这个路径该…