【初阶数据结构】——二叉树OJ练习

news2024/11/25 23:28:37

文章目录

    • 前言
    • 1. 单值二叉树
      • 思路分析
        • 思路1. 遍历对比
        • 思路2. 递归
      • 代码实现
    • 2. 判断两棵二叉树是否相同
      • 思路分析
      • 代码实现
    • 3. 另一棵树的子树
      • 思路分析
      • 代码实现
    • 4. 返回二叉树结点的前序遍历数组
      • 思路分析
      • 代码实现
    • 5. 对称二叉树
      • 思路分析
      • 代码展示

前言

上一篇文章我们刚刚学完二叉树初阶的相关知识,那接下来我们就趁热打铁,这篇文章,我们一起来看几道二叉树相关的OJ练习。
在这里插入图片描述

1. 单值二叉树

把题目链接给大家,供大家练习链接: link
我们一块看一下题:
在这里插入图片描述

思路分析

题目让我们干啥呢?

给定一棵二叉树,我们要写一个程序判断出它是不是一棵单值二叉树(即所有结点的值都相同的二叉树)。

应该怎么搞?

思路1. 遍历对比

首先第一种思路,就非常简单粗暴,我们可以记录一下根结点的值,然后,遍历这棵二叉树,去拿每个结点的值与根结点做对比,如果都相等,那就证明它是一棵单值二叉树,返回true;
一旦有一个不相等,那就证明不是,返回false。

这种方法呢我们就不实现具体的代码了,有兴趣大家可以自己写一下,接下来看另一种思路。

思路2. 递归

🆗,二叉树的结构我们还是更适合用递归的思想来搞。
以题目给的这棵二叉树为例:
在这里插入图片描述
首先要知道在这里如果是空树我们也应该认为它是一棵单值二叉树。
然后如果不是空,我们就开始判断,怎么判断?
如果根结点的值等于其左右结点的值,那就证明当前这几个结点是满足的,当然左右子树可能为空,为空的话就不比较了,只比较存在的那一边;
然后,去递归比较它的左右子树,还是同样的方法去比较,如果左右子树也满足,就返回true,有不满足的就false。

代码实现

写代码的时候呢,我们也有两种方式:

第一种就是按照上面的思想写出相等的情况,进行判断

bool isUnivalTree(struct TreeNode* root){
    if(root)
    {
        if(root->left&&root->right)
        {
            if((root->val==root->left->val)&&(root->val==root->right->val))
            {
                bool ret1=isUnivalTree(root->left);
                bool ret2=isUnivalTree(root->right);
                if(ret1&&ret2)
                    return true;
            }
            return false;
        }
        if(root->left)
        {
            if((root->val==root->left->val))
            {
                if(isUnivalTree(root->left))
                    return true;
            }
            return false;
        }
        if(root->right)
        {
            if((root->val==root->right->val))
            {
                if(isUnivalTree(root->right))
                    return true;
            }
            return false;
        }
    }
    return true;
}

但是:

这样去判断成立的情况写着有点麻烦,所以我们可以判断不成立的情况:

bool isUnivalTree(struct TreeNode* root){
    if(root)
    {
        if(root->left&&root->val!=root->left->val)
            return false;
        if(root->right&&root->val!=root->right->val)
            return false;
        return isUnivalTree(root->left)&&isUnivalTree(root->right);
    }
    return true;
}

这样代码就简洁很多。
在这里插入图片描述

2. 判断两棵二叉树是否相同

题目链接: link
在这里插入图片描述

思路分析

这道题呢,是给我们两棵二叉树的根结点,我们的程序要能够判断出这两棵二叉树是否相同(这里的相同指两个树结构相同,且对应结点的值相同)。

下面我们讲一下思路:

还是递归:
被对比的两棵二叉树有一下3种情况:

  1. 两棵树都不为空
    这时我们先判断根结点的值是否相同,不相同直接返回false;
    如果相同,就要继续比较,怎么比?
    递归判读如果两棵树的左子树相同且右子树相同,那么两棵树就完全相同,左右子树有一个不相同就返回false。
  2. 两棵中有一棵为空,一棵不为空
    不管是一开始两棵中有一棵为空,还是在递归过程中一棵空,一棵不为空,那两棵树都不可能相同了,返回false。
  3. 两棵树都为空
    都是空树也算相同,返回true

代码实现

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    //p,q都不为空
    if(p&&q)
    {
        if(p->val!=q->val)
            return false;
        return isSameTree(p->left,q->left)
        &&isSameTree(p->right,q->right);
    }
    //p,q都为空
    if(p==NULL&&q==NULL)
        return true;
    //p,q有一个空
    return false;
}

在这里插入图片描述

3. 另一棵树的子树

题目链接: link
在这里插入图片描述

思路分析

这道题也是给我们两棵二叉树,这次是让我们判断第二棵树是不是第一棵树的子树。
首先题目中需要注意的是
在这里插入图片描述
也就是说这种情况是不算的
在这里插入图片描述

🆗,那这道题该怎么解决:

我们刚刚完成了第二道题,判断两棵树是否相同,那现在再去做这个题是不是就很省事了啊。
我们可以遍历第一棵树,将第一棵树每一个结点对应的子树都与第二棵树进行比较,如果有一个相同的,那就说明第二棵树是第一棵树的子树。(判断两棵树是否相同直接复用上一题的代码)

代码实现

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    //p,q都不为空
    if(p&&q)
    {
        if(p->val!=q->val)
            return false;
        return isSameTree(p->left,q->left)
        &&isSameTree(p->right,q->right);
    }
    //p,q都为空
    if(p==NULL&&q==NULL)
        return true;
    //p,q有一个空
    return false;
}

bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
    if(root)
    {
        if(isSameTree(root,subRoot))
            return true;
        return isSubtree(root->left,subRoot)||isSubtree(root->right,subRoot);
    }
    return false;
}

在这里插入图片描述
根据题目subroot是不为空的,所以root为空时两者不可能相同。
在这里插入图片描述

4. 返回二叉树结点的前序遍历数组

题目链接: link
在这里插入图片描述

思路分析

这道题呢在leetcode上的名字是二叉树的前序遍历,但是跟我们平时正常的前序遍历还不太一样。
我们之前的前序遍历是打印一下结点,但这道题要求我们把二叉树的结点以前序遍历的顺序放到一个数组中,然后返回这个数组。
并且呢,题目要求:
在这里插入图片描述
这个数组必须是malloc的。

那怎么搞呢?

既然要放在数组里,我们肯定要先定义一个数组,那数组应该开多大呢?
我们可以直接开一个大小100的。
在这里插入图片描述
因为题目明确了结点个数在100以内。
但是我们最终还是要知道数组的大小,因为题目给的接口函数还提供了另一个参数:
在这里插入图片描述
这个参数就是用来获取数组大小的,虽然题目没有明确要求让我们返回数组的大小。

所以:

我们就干脆单独写一个求结点个数的函数,这个我们上一篇文章刚学过,简简单单:

 //计算树的结点个数
 int TreeSize(struct TreeNode* root)
 {
     return root==NULL?0:
     TreeSize(root->left)+TreeSize(root->right)+1;
 }

那求完结点个数,参数returnsize的值我们就知道了,数组我们也可以malloc开辟了:
在这里插入图片描述

那接下来就是前序遍历结点放到数组里了

那还是用递归啊,但是直接在preorderTraversal函数里进行递归可以吗,好像不行,如果直接拿preorderTraversal函数递归,还有returnsize这个参数,还有malloc开辟的数组,好像没法搞。
那怎么办?

干脆再写一个函数来单独处理这个数组。

那要完成的工作就是前序遍历二叉树,按顺序把结点放入数组就行了。
这还不简单,不就写个前序遍历嘛,只不过不是打印结点,而是把结点的值存到数组中。

思路理清,我们来写一下代码

代码实现

 //计算树的结点个数
 int TreeSize(struct TreeNode* root)
 {
     return root==NULL?0:TreeSize(root->left)+TreeSize(root->right)+1;
 }

//前序遍历树的结点放入数组
void preorder(struct TreeNode* root,int* arr,int* pi)
{
    if(root)
    {
        arr[(*pi)++]=root->val;
        preorder(root->left,arr,pi);
        preorder(root->right,arr,pi);
    }
}

int* preorderTraversal(struct TreeNode* root, int* returnSize)
{
    *returnSize=TreeSize(root);
    int* arr=(int*)malloc(*returnSize*sizeof(int));
    int i=0;
    preorder(root,arr,&i);
    return arr;
}

在这里插入图片描述

5. 对称二叉树

题目链接: link
在这里插入图片描述

思路分析

我们一起来分析一下这道题:

题目呢是让我们去判断一棵二叉树是否是对称二叉树。
🆗,那怎么判断,是判断它的左右子树相同吗?
不是的。
对称二叉树的特点是什么呢?
在这里插入图片描述
我们仔细观察可以发现:
对于对称二叉树来说,对称轴左边的子树和右边的子树,满足的是左子树的左孩子和右子树的右孩子对称,而左子树的右孩子和右子树的左孩子对称。
所以我们判断一棵二叉树是不是对称二叉树,不是判断它的左右子树是否相同,而应该反着比,看一个结点的左子树是不是和另一个结点的右子树对称。

在这里插入图片描述
我们拿到的是一棵二叉树的根,那我们是不是只需判断它的左右子树是否对称就行了啊。
所以呢,我们再搞一个子函数出来,专门用来判断两棵树是否对称。

在这里插入图片描述
然后我们注意到:
在这里插入图片描述
题目说明了给的二叉树至少一个结点,不会是空树。
那我们就可以直接这样:
在这里插入图片描述
如果它的左右子树对称,那整棵树不就对称了嘛。

那接下来就来实现判断两棵树是否对称的子函数_isSymmetric就行了:

首先如果两棵树都为空,那这棵二叉树就只有一个根结点嘛,那也算对称的:
在这里插入图片描述
如果其中有一个不为空,另一个为空,那肯定就不对称了:
在这里插入图片描述
那再往下,就是两棵树都不为空了,那首先是不是要判断它们的根结点对应的值想不想同啊,如果不相同,那也不对称:

在这里插入图片描述

那相同的话,是不是就要继续判断了,怎么判断?
那根据上面的分析,是不是要判断root1的左子树和root2的右子树是否对称,以及root1的右子树和root2的左子树是否相同。
如果都满足,那就是对称了
在这里插入图片描述
那就实现完了

在这里插入图片描述

代码展示

//判断两棵树是否对称
bool _isSymmetric(struct TreeNode* root1,struct TreeNode* root2){
    if(root1==NULL&&root2==NULL)
        return true;
    if(root1==NULL||root2==NULL)
        return false;
    if(root1->val!=root2->val)
        return false;
    return _isSymmetric(root1->left,root2->right)
        &&_isSymmetric(root1->right,root2->left);
}

bool isSymmetric(struct TreeNode* root){
    return _isSymmetric(root->left,root->right);
}

在这里插入图片描述
以上就是几道与二叉树相关的OJ练习。
在这里插入图片描述

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

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

相关文章

使用nginx的rtmp模块搭建RTMP和HLS流媒体服务器

使用nginx的rtmp模块搭建RTMP和HLS流媒体服务器 文章目录使用nginx的rtmp模块搭建RTMP和HLS流媒体服务器环境搭建参数配置验证结果前面文章中已经介绍了《使用nginx搭建rtmp流媒体服务器》和《使用nginx搭建HLS服务器》,其实nginx的RTMP模块本身就支持接收RTMP推流、…

【jQuery】实现文件上传和loading效果

一、 jQuery实现文件上传1. 定义UI结构<!-- 导入 jQuery --><script src"./lib/jquery.js"></script><!-- 文件选择框 --><input type"file" id"file1" /><!-- 上传文件按钮 --><button id"btnUplo…

我们是存算一体化

从最初的计算和存储分离,随着技术的发展,存算一体化越来越被大家重视,成为了下一个发展浪潮。其实对于海量数据场景来说,我们更认为数据应该是存算协同的关系。存算一体化才是最高效的技术之一,但是目前真正的存算一体,或者说革命性地突破冯•诺伊曼架构的存算一体还未实…

考虑电能交互的冷热电区域多微网系统双层多场景协同优化配置(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

力扣sql基础篇(十)

力扣sql基础篇(十) 1 矩形面积 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # 纵坐标相同或者横坐标的两个点是不可能成为矩形的 # 使用inner join连接两个表的时候 考虑需不需要去重,如何去重也是很重要的(会有重复的两条数据,只是顺序不一样) S…

振弦采集模块配置工具VMTool通用串口调试模块

振弦采集模块配置工具VMTool通用串口调试模块 VMTool 扩展功能 双击主界面右侧扩展工具条可实现扩展功能区的显示与隐藏切换。 扩展功能包括串口调试、MODBUS、实时曲线及数据存储等几个功能模块。 扩展功能区显示效果如下。 串口调试模块直接使用当前已连接的 COM 端口&#…

redis集群启动

文章目录一、添加配置文件二、启动服务和集群三、集群操作四、故障恢复一、添加配置文件 一共8个文件 创建6个redisXXX.conf文件 6个文件的内容和下面的一样&#xff0c;但是要修改端口数值。例如&#xff1a;把下面的6379全部改为6380# 路径为redis.conf的绝对路径 include…

易基因2022年度DNA甲基化研究高分项目文章精选

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。回顾刚刚过去的2022年&#xff0c;易基因参与的DNA甲基化研究在细胞分化与发育、疾病发生发展及标志物筛选、环境因素暴露与响应等应用场景成果层出不穷&#xff0c;小编选取其中三个研究方…

挖掘HTTP请求走私漏洞

利用Burp插件挖掘HTTP请求走私 HTTP请求走私通常遗留在漏洞发现赏金项目中。但通过正确的插件&#xff0c;您 可以在下一个赏金项目中自动化地完成挖掘HTTP请求走私漏洞的过程。 了解HTTP请求走私 现代网站经常部署多个代理服务器用于转发用户请求到托管Web应用程序的真实服务…

linux free命令

free是指查看当前系统内存的使用情况&#xff0c;它显示系统中剩余及已用的物理内存和交换内存&#xff0c;以及共享内存和被核心使用的缓冲区。 选项&#xff1a; -b&#xff1a;以字节为单位显示。 -k&#xff1a;以K字节为单位显示。 -m&#xff1a;以兆字节为单位显示。 参…

JavaScript 库之 dyCalendarJS(日历)

JS 库之 dyCalendarJS&#xff08;日历&#xff09;&#xff09;参考获取使用导入CSSJS使用HTMLJavaScript代码总汇样式容器圆边颜色渐变阴影日历dycalendar.draw()举个栗子默认样式daymonth其他参考 项目描述DYClassRoom前往GitHub前往 获取 GitHub dyCalendarJSFrom jsDeli…

拼一个自己的操作系统(SnailOS 0.03的实现)

像文本模式一样显示字符串在拼操作系统的征程中&#xff0c;仅仅是画上一些简单的图形&#xff0c;显然是不够的。原因就在于&#xff0c;如果开发的过程中&#xff0c;出现了“臭虫”&#xff0c;而系统并不能显示任何有价值的信息&#xff0c;那我们岂不是两眼一抹黑&#xf…

【电子学会】2022年12月图形化四级 -- 简易抗疫物资管理系统

简易抗疫物资管理系统 1. 准备工作 (1)角色:从角色库中添加4个按钮,添加文字“增加”、“删除”、“修改”、“查询”,修改角色名字为“增加按钮”、“删除按钮”、“修改按钮”、“查询按钮”; (2)列表:新建列表“抗疫物资清单”。 2. 功能实现 (1)点击“增加按…

第三章 Linux中的shell与权限

第三章 Linux中的shell与权限一、linux的内核&#xff08;kernel&#xff09;与外壳&#xff08;shell&#xff09;1、内核与外壳的关系2、外壳的作用二、权限1、用户中的权限&#xff08;1&#xff09;超级用户&#xff1a;root&#xff08;2&#xff09;普通用户a.普通用户的…

超实用的微信公众号内容运营方案分享

公众号运营的本质就是图文生产&#xff0c;内容绝对是涨粉引流的关键。没有产出好的内容&#xff0c;这个公众号是绝对走不长远的。 公众号内容运营大致上可以分为两个大方向&#xff0c;一个是搭建完整的公众号内容体系&#xff0c;一个是创作具体的公众号推文内容&#xff0…

Sklearn标准化和归一化方法汇总(2):Min-Max归一化

Sklearn中与特征缩放有关的五个函数和类&#xff0c;全部位于sklearn.preprocessing包内。作为一个系列文章&#xff0c;我们将逐一讲解Sklearn中提供的标准化和归一化方法&#xff0c;以下是本系列已发布的文章列表&#xff1a; Sklearn标准化和归一化方法汇总(1)&#xff1a…

【现代机器人学】学习笔记十:机器人控制

这节的内容主要讲述在关节空间和任务空间中的运动控制中的反馈控制&#xff0c;力控制&#xff0c;运动-力混合控制以及阻抗控制、导纳控制&#xff0c;pid控制等内容。在之前的内容当中&#xff0c;往往不涉及到实际对机器人的操纵&#xff0c;即我们计算出一个结果&#xff0…

【5】KubeSphere部署应用 | MySQL

目录 1、部署的架构 2、KubeSphere几个主要的模块 3、部署MySQL 【1】先创建MySQL的配置文件 【2】创建存储卷 【3】部署有状态服务 【4】查看创建的服务 【5】创建一个服务可以在集群外可以访问 1、部署的架构 2、KubeSphere几个主要的模块 KubeSphere的工作负载相当于k8s里…

算法之常见字符串题目

leedcode344. 反转字符串 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间&#xff0c;你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 示例 1&#xff1a; 输入&#xff1a;s […

进程学习笔记

进程 定义 一个程序程序在一个数据集合上的动态执行过程 与程序区别 动静&#xff0c;暂时的过程和永久的存在&#xff0c;进程由程序、数据、进程控制块&#xff08;PCB&#xff09;组成 特性 动态并发&#xff08;进程&#xff09;独立&#xff08;分页有力支持&#x…