链式二叉树OJ题思路分享

news2024/11/15 11:47:16

⏩博主CSDN主页:杭电码农-NEO⏩

⏩专栏分类:刷题分享⏪

⏩代码仓库:NEO的学习日记⏩

🌹关注我🫵带你刷更多C语言和数据结构的题!
  🔝🔝


在这里插入图片描述


链式二叉树OJ题分享

  • 1. 前言🚩
  • 2. 单值二叉树🚩
    • 2.1 审题🏁
    • 2.2 代码实现🏁
  • 3. 检查两颗树是否相同🚩
    • 3.1 审题🏁
    • 3.2 代码实现🏁
  • 4. 对称二叉树🚩
    • 4.1 审题🏁
    • 4.2 代码实现🏁
  • 5. 二叉树的前中后序遍历🚩
    • 5.1 审题🏁
    • 5.2 代码实现🏁
  • 6. 总结🚩


1. 前言🚩

链式二叉树这一个板块的考题还是比较多的,这里我给大家分享几道很经典的OJ题,仅供参考! 分别是 1. 单值二叉树力扣965题----- 2. 检查两棵树是否相同力扣100题 ----- 3. 对称二叉树 力扣101题


2. 单值二叉树🚩

2.1 审题🏁

在这里插入图片描述

这个题很简洁,让我们判断一个二叉树所有节点的值是否相同,这里的题我们都最好用递归的思想解决,因为非递归往往比较困难.我们先从易到难.我们可以想到的思路是:

  • 如果二叉树为空树,我们应该返回true

当前节点不为空时,我们就要用分治的思想,先判断当前节点的左孩子是不是NULL节点,如果不是空节点就将当前节点存储的值与左孩子节点存储的值进行比较右边也是如此

  • 判断左右子树是否为空,不为空的子树就判断当前节点与子树节点存储的值是否相同

这种分治思想可以理解为我们当前节点与孩子节点的值相同后又去比较孩子节点与孩子的孩子节点是否相同,如果遇见不相同的返回false,相同就一直往下走直到遇见NULL


2.2 代码实现🏁

我们可以根据前面的两个判断写出这样的代码:

bool isUnivalTree(struct TreeNode* root)
{
    if(root==NULL)//如果树为空或当前节点为空就返回true
    {
        return true;
    }
    if(root->left!=NULL&&root->val!=root->left->val)
    {
        return false;
    }
    if(root->right!=NULL&&root->val!=root->right->val)
    {
        return false;
    }
}

这里需要注意的是,当 root==NULL时有两种情况,一是我们拿到的树本身就是一个空树,我们返回true是没有问题的,二是我们往后递归下去的某个节点是空节点,走到空节点代表这条路线上所有节点的值都一样,返回true也没问题

然而当我们走完上面代码后,既没有返回true也没有返回false,走到这里就代表此节点即不为空,并且它和它孩子的值也是相同的,所以这里我们就去到它的左右子树再次重复上面的过程

bool isUnivalTree(struct TreeNode* root)
{
    if(root==NULL)
    {
        return true;
    }
    if(root->left!=NULL&&root->val!=root->left->val)
    {
        return false;
    }
    if(root->right!=NULL&&root->val!=root->right->val)
    {
        return false;
    }
    return isUnivalTree(root->left)&&isUnivalTree(root->right);//左右子树的值都相同才返回true

}

3. 检查两颗树是否相同🚩

3.1 审题🏁

在这里插入图片描述

判断两颗树是否相同,它不仅仅是值相同,值对应的左右孩子的顺序也要相同,这里我们还是采用分治递归的思想,把大事化小,把小事化了,这里我们首先可以想到的是,当着两棵树都为空树时,它们是相同的返回true.然而这里还会出现一种情况,那就是一棵树为空,另外一颗树不为空,这时它们是不相同的,返回false.当走完上面两个条件后,说明此时这两个节点都不为空,我们就接着判断在这个节点这两棵树的值是否相同

我们可以根据上面的讨论得到这样的结论:

  • 当两个树的节点都为空时返回true
  • 当两个树中一个节点为空另一个节点为非空返回false
  • 当两棵树都不为空,判断它们的值是否相同

3.2 代码实现🏁

我们根据上面的讨论可以写出这样代码:

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

当代码走完上面的步骤后没有返回true也没有返回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;
    }
    bool left = isSameTree(p->left,q->left);
    bool right = isSameTree(p->right,q->right);
    if(!left||!right)//如果左子树和右子树其中一个的结果是false,那么就返回false
    {
        return false;
    }
    else
    return true;
}

这里需要注意的点是,我们递归时是将节点一的左子树和节点二的左子树放在一起递归,节点一的右子树和节点二的右子树放在一起递归.这里不能搞混淆了.并且左右子树中只要有一个false,那么就直接返回false,因为只有节点一和节点二不相同时才会返回返回false给left或right

如果左右子树都不为false,那么就代表此节点的左右子树与另外一棵树的对应节点的左右子树是相同的


4. 对称二叉树🚩

4.1 审题🏁

在这里插入图片描述

这个题和判断相同的二叉树很相似,上一题是左边与左边做判断,这个题要左边和右边做判断,如果是对称的二叉树的话,那么当前节点的左子树要等于当前节点的右子树,然后左子树的左子树要等于右子树的右子树,左子树的右子树要对应右子树的左子树,很绕.但是仔细一下确实是这样.我们还是用分治的思想,第一步如果当前节点的左孩子和右孩子都为空,或者说这棵树只有一层,那么它是对称的,返回true.第二步如果当前节点的左孩子或者右孩子有一个为空,一个不为空,那么这棵树就不是对称的,返回false.当我们走完上面两步还没有如何返回值时,证明左孩子和右孩子都不为空,这时就要判断左右孩子的值是否相同.

根据上面的思路总结:

  • 左右孩子都为空,返回true
  • 左右孩子其中一个为空另一个不为空返回false
  • 当左右孩子都不为空时比较左右孩子对应的值是否相同

4.2 代码实现🏁

需要注意的是,这里的接口函数的参数只有一个,但是我们递归的时候想要递归左子树的左子树与右子树的右子树,我们就需要两个参数,所以我们不能在原有的接口函数上进行递归,这里我们应该自己写一个函数用来递归

bool isSymmetric(struct TreeNode* root)
{
    if(root==NULL)
    {
        return true;
    }
    struct TreeNode* left=root->left;
    struct TreeNode* right=root->right;
    return myis(left,right);
}

这里我们先把接口函数的功能给实现了,然后再来看我们的自定义函数:

bool myis(struct TreeNode* left,struct TreeNode* right)
 {
     if(left==NULL&&right==NULL)
     {
         return true;
     }
     if(left==NULL||right==NULL)
     {
         return false;
     }
     if(left->val!=right->val)
     {
         return false;
     }
}

将上面所有代码走完后,如果还是没有返回值的话,证明此时的左节点和右节点即不为空,并且它们的值相同,所以我们就接着往下递归,不断重复上面的过程

bool myis(struct TreeNode* left,struct TreeNode* right)
 {
     if(left==NULL&&right==NULL)
     {
         return true;
     }
     if(left==NULL||right==NULL)
     {
         return false;
     }
     if(left->val!=right->val)
     {
         return false;
     }
     
     return myis(left->left,right->right)&&myis(left->right,right->left);
 }

这里递归的对象是左子树的左子树和右子树的右子树是一队,而左子树的右子树和右子树的左子树是一队.并且要它们两个都返回true时,才是真正对称的,所以用&&符号连接.


5. 二叉树的前中后序遍历🚩

这里我们只需要把二叉树的前序遍历的题目给理解了,中后序自然就简单了.


5.1 审题🏁

在这里插入图片描述

首先我们要先知道接口函数的参数int* returnSize.这里它实际上不是告诉我们二叉树有多少个节点的意思,而是要我们自己求出二叉树的节点个数,然后解引用returnSize修改returnSize的值,最后调用接口函数和returnsize的值判断我们的代码正不正确.所以这里我们可以自定义一个函数来求二叉树节点的个数

其次,这里要返回一个数组,数组里元素的顺序是二叉树前序遍历的顺序,所以这里我们要动态开辟一个数组.


5.2 代码实现🏁

根据上面两个思路我们可以写出这样的代码:

int TreeSize(struct TreeNode* root)//求二叉树节点个数的函数
 {
     return root==NULL?0:TreeSize(root->left)+TreeSize(root->right)+1;
 }
 int* preorderTraversal(struct TreeNode* root, int* returnSize)
{
    int size=TreeSize(root);
    int* a=(int*)malloc(sizeof(int)*size);
    *returnSize=size;//将二叉树节点个数赋值给returnSize

这时求二叉树节点个数的函数,如果这里你不理解这个函数是怎么实现的,可以跳转二叉树详解,或者自己画递归展开图来理解.

写到这一步后,我们想要在数组中存储元素就要定义下标,而如果直接在要递归的函数中定义下标的话,每次递归后下标都会重置为0,就相当于每次都在操作数组里的第一个元素,所以这里我们采用了一个很巧妙的方式,将定义下标的位置写在递归函数外,并且将下标的地址传进函数,这样函数不管递归多少次下标都不会出问题:

int TreeSize(struct TreeNode* root)
 {
     return root==NULL?0:TreeSize(root->left)+TreeSize(root->right)+1;
 }

 void _preorderTraversal(struct TreeNode* root,int* a,int* pi)//递归的函数
 {
     if(root==NULL)//如果节点为空就直接返回
     {
         return;
     }
     a[(*pi)++]=root->val;//前序遍历,先将节点的值给数组a,再递归左右孩子
     _preorderTraversal(root->left,a,pi);
     _preorderTraversal(root->right,a,pi);

 }
int* preorderTraversal(struct TreeNode* root, int* returnSize)
{
    int size=TreeSize(root);
    int* a=(int*)malloc(sizeof(int)*size);
    int i=0;//在接口函数中定义下标
    _preorderTraversal(root,a,&i);//将下标i的地址传入要递归的函数
    *returnSize=size;
    return a;//最后返回数组a的地址

}

当我们理解了前序遍历后,中后序遍历也就是将节点保存在数组中的位置的代码和遍历左右孩子的代码换一下就可以了! 中序遍历的链接:力扣94题 后序遍历的链接: 力扣145题


6. 总结🚩

这一部分的题用递归的方法比非递归更加容易写出代码,但是不好理解,所以画递归展开图是很有必要的.主要思想就是分治,大事化小小事化了.


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

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

相关文章

macOS Ventura 13.5beta OpenCore黑苹果双引导分区原版镜像

镜像特点(原文地址:http://www.imacosx.cn/113700.html,转载请注明出处) 完全由黑果魏叔官方制作,针对各种机型进行默认配置,让黑苹果安装不再困难。系统镜像设置为双引导分区,全面去除clover引…

六级备考25天|CET-6|听力第五讲|演讲满分技巧|分值最高|2022年6月考题16-18题|18:15~19:00

14.2分一题 抓重点 目录 2. 听力原文复现 问题16 问题17 问题18 3. 听力原文重点词汇 问题16 问题17 问题18 2. 听力原文复现 问题16 What does the speaker say about most American hospitals? visit brief useless Dont challenge with the authority unle…

HEVC熵编码核心点介绍

熵编码基本原理 消息与信息 把客观物质运动和主观思维的活动状态表达出来就成为了消息;消息中包含信息,是信息的载体;因此,信息与消息既有区别又有联系的; 获取信息的过程就是一个消除或部分消除不确定性的过程&…

Linux网络基础-4

在之前的网络基础博客中,我们对网络进行了概要解释,了解了应用层和传输层的知名协议。接下来我们来对网络层的典型协议进行解析。 目录 1.网络层协议 2.IP协议 2.1协议格式 2.2地址管理 2.3特殊网络 2.3.1私网的组建 2.3.2特殊IP地址 2.4路由选…

ACP(MaxCompute篇)-Tunnel上传下载数据

概述 相关命令 odpscmd里面包含了tunnel test11_123>tunnel help; Usage: tunnel <subcommand> [options] [args] Type tunnel help <subcommand> for help on a specific subcommand.Available subcommands:upload (u)download (d)resume (r)show (s)purge …

探索SpringBoot 3.1的惊艳新特性

一、介绍 1.1 新特性概述 经过半年的沉淀 Spring Boot 3.1于2023年5月18日正式发布了&#xff0c;带来了许多令人兴奋的新特性和改进。本篇博客将详细介绍Spring Boot 3.1的新特性、升级说明以及核心功能的改进。 同时&#xff0c;2.6.x 版本线已经停止维护了&#xff0c;最新…

《消息队列高手课》课程笔记(二)

消息模型&#xff1a;主题和队列有什么区别&#xff1f; 两类消息模型 早期的消息队列&#xff0c;就是按照“队列”的数据结构来设计的。 生产者&#xff08;Producer&#xff09;发消息就是入队操作&#xff0c;消费者&#xff08;Consumer&#xff09;收消息就是出队也就是…

数字逻辑习题

第一次作业 第二次作业 第三次作业 卡诺图最小项 计算模数M考察74LS161的特点 计数器数据选择器 在A和C加上非门即可 求计数器的模数M 该计数器是环形计数器&#xff08;循环右移&#xff09;&#xff0c;模数根据初始状态而定 由n个移位寄存器构成的环形计数器最大可能模值为n…

韩流在元宇宙中崛起,感受韩国文化的魅力!

大获成功&#xff01;第一波 K-verse 土地全部售罄&#xff01; 多家领先韩国文化公司&#xff0c;包括 SMBM、CUBE 娱乐、LINE Studio、Gravity、乐天世界、K League 等进驻 The Sandbox&#xff0c;并在第一波 K-verse 土地销售活动上发布了邻近这些公司的 LAND。 所有土地全…

哈希表(散列表)详解

&#x1f495;**今天的每一秒都是珍贵的&#xff0c;因为它永远不会再次出现。**&#x1f495; &#x1f43c;作者&#xff1a;不能再留遗憾了&#x1f43c; &#x1f386;专栏&#xff1a;Java学习&#x1f386; &#x1f697;本文章主要内容&#xff1a;深入理解哈希表&#…

npm init和npm create、npm create vite什么意思

npm init 和npm create 之前在用npm时&#xff0c;一直都是npm init来初始化一个项目并生成package.json文件。 但是&#xff0c;今天在看vite的官方文档时&#xff0c;vite上说创建一个vite项目的命令是 npm create vitelatest之前一直没有用过npm create这个命令&#xff…

sql ---- 查询两个日期间隔的天数:Datediff(a,b)

197. 上升的温度 表&#xff1a; Weather ------------------------ | Column Name | Type | ------------------------ | id | int | | recordDate | date | | temperature | int | ------------------------ id 是这个表的主键 该表包含特定…

Windows编程开发中的语句覆盖、条件覆盖、判定覆盖、条件-判定覆盖、组合覆盖、路径覆盖

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天总结一下Windows编程开发中的语句覆盖、条件覆盖、判定覆盖、条件-判定覆盖、组合覆盖、路径覆盖。 首先你要明白一点&#xff1a; 逻辑覆盖率&#xff1a;语句覆盖<条件覆盖<判定覆盖<条件-判…

Prometheus+Grafana学习(十一)安装使用pushgateway

Pushgateway允许短暂和批量作业将其指标暴露给 Prometheus。由于这些工作的生命周期可能不足够长&#xff0c;不能够存在足够的时间以让 Prometheus 抓取它们的指标。Pushgateway 允许它们可以将其指标推送到 Pushgateway&#xff0c;然后 Pushgateway 再将这些指标暴露给 Prom…

算法套路十八——区间 DP

算法套路十八——区间 DP 线性DP: 具有前缀/后缀结构的问题&#xff0c;其中每个阶段只依赖于前一阶段的状态区间DP:需要确定给定区间内所有可能状态的问题&#xff0c;并从较小区间向较大区间进行转移。 区间DP介绍&#xff1a;https://oi-wiki.org/dp/interval/ 算法示例&…

网络安全证书合集

网络安全作为2022年十大高新紧缺技能之一&#xff0c;足以证明网络安全的重要性&#xff0c;作为网络安全工程师更是责任重大&#xff0c;证书作为能力证明的一方面&#xff0c;也是尤为重要&#xff0c;本文将着重盘点一下网络安全证书合集。 国家注册渗透测试工程师&#xf…

传染病学模型 | Matlab实现SEIR传染病学模型 (SEIR Epidemic Model)

文章目录 效果一览基本描述模型介绍程序设计参考资料效果一览 基本描述 传染病学模型 | Matlab实现SEIR传染病学模型 (SEIR Epidemic Model) 模型介绍 SEIR模型是一种常见的传染病传播模型,用于描述人群感染某种传染病的过程。SEIR模型将人群划分为四个互相转化的状态: 易感者…

内容好但流量差?B站流量密码可能就在这

B站知名数码UP主老师好我叫何同学&#xff08;以下简称“何同学”。&#xff09;时隔两个月再次更新&#xff0c;这支标题为《为了找到流量密码&#xff0c;我们做了个假B站...》的视频不仅吸引了观众的围观&#xff0c;更是获得了众多B站UP主们的“声援”。 如题所见&#xf…

Netty 源码分析系列(十八)一行简单的writeAndFlush都做了哪些事?

文章目录 前言源码分析ctx.writeAndFlush 的逻辑writeAndFlush 源码ChannelOutBoundBuff 类addMessage 方法addFlush 方法AbstractNioByteChannel 类 小结 前言 对于使用netty的小伙伴来说&#xff0c;我们想通过服务端往客户端发送数据&#xff0c;通常我们会调用ctx.writeAn…

SURF算法详解

Speeded Up Robust Features&#xff08;SURF&#xff0c;加速稳健特征&#xff09; 一&#xff0e;积分图像 1.什么是积分图像 积分图像是输入的灰度图像经过一种像素间的累加运算得到种新的图像媒介。对于一幅灰度的图像&#xff0c;积分图像中的任意一点&#xff08;x,y&…