代码随想录算法训练营Day|层序遍历,翻转二叉树,对称二叉树

层序遍历

层序遍历一个二叉树,就是从左往右一层一层的遍历二叉树,这种遍历方式需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。而这种层序遍历方式就是图论中的广度优先遍历,只不过我们用在二叉树上。

使用队列实现二叉树广度优先遍历。

class Solution{
public:vector<vector<int>>levelOrder(TreeNode* root){queue<TreeNode*>que;if(root != NULL)que.push(root);vector<vector<int>>result;while(!que.empty()){int size = que.size();vector<int>vec;//这里使用固定大小size,不要使用que.size().因为que.size是不断变化的for(int i = 0;i < size; i++){TreeNode* node = que.front();que.pop();vec.push_back(node->val);if(node->left)que.push(node->left);if(node->right)que.push(node->right);}result.push_back(vec);}return result;}
};
//递归法
class Solution{
public:vod order(TreeNode* cur,vector<vector<int>>&result,int depth){if(cur == nullptr)return;if(result.size() == depth)result.push_back(vector<int>());result[depth].push_back(cur->val);order(cur->left,result,depth + 1);order(cur->right,result,depth + 1);}vecctor<vector<int>>levelOrder(TreeNode* root){vector<vector<int>>result;int depth = 0;order(root,result,depth);return result;}
};

反转二叉树

思路:只要把每个节点的左右孩子翻转一下,就可以达到整体翻转的效果。这道题使用前序遍历和后序遍历都可以,唯独中序遍历不方便,因为中序遍历会把某些节点的左右孩子翻转两次。层序遍历也可以。

递归三步:

1.确定递归函数的参数和返回值

参数就是要传入节点的指针,不需要其他参数了,通常此时定下来主要参数,如果在写递归的逻辑中发现还需要其他参数的时候,随时补充。

返回值的话其实也不需要,但是题目中给出的要返回root节点的指针,可以直接使用题目定义好的函数,所以就函数的返回类型TreeNode*。

TreeNode* invertTree(TreeNode* root)

2.确定终止条件

当前节点为空的时候,就返回

if(root == NULL)return root;

3.确定单层递归的逻辑

因为先前序遍历,所以先进行交换左右孩子节点,然后左转左子树,反转右子树。

swap(root->left,root->right);
invertTree(root->left);
invertTree(root->right);
//基于上面三步,完整C++代码:class Solution{
public:TreeNode* invertTree(TreeNode* root){if(root == NULL)return root;swap(root->left,root->right); //中invertTree(root->left); //左invertTree(root->right);  //右return root;}
};

迭代法


深度优先遍历

前序遍历:

class Solution{TreeNode* invertTree(TreeNode* root){if(root == NULL)retrun root;stack<TreeNode*>st;st.push(root);while(!st.empty()){TreeNode* node = st.top();  //中st.pop();swap(node->left,node->right);if(node->right)st.push(node->right); //右if(node-left)st.push(node->left); //左}return root;}
};

前序遍历:

class Solution{
public:TreeNode* invertTree(TreeNode* root){stack<TreeNode*>st;if(root != NULL)st.push(root);//判断非空,栈尾部添加元素while(!st.empty()){TreeNode* node = st.top(); //top()取出栈顶元素if(node != NULL){st.pop();  //pop()删除栈顶元素if(node->right)st.push(node->right);//右if(node->left)st.push(node->left);//左st.push(node);st.push(NULL);  //中}else{st.pop();node = st.top();st.pop();swap(node->left,node->right);  //节点处理逻辑}}return root;}
};

广度优先遍历

//层序遍历可以翻转这棵树,因为层序遍历也可以把每个节点的左右孩子都翻转一遍,代码如下:
class Solution{
public:TreeNode* invertTree(TreeNode* root){queue<TreeNode*>que;if(root != NULL)que.push(root);while(!que.empyu()){int size = que.size();for(int i = 0;i < size;i++){TreeNode* node = que.front();que.pop();swap(node->left,node->right);//节点处理,左右交换if(node->left)que.push(node->left);if(node->right)que.push(node->right);}}return root;}
};

不能用递归的中序遍历,因为使用递归的中序遍历,某些节点的左右孩子会翻转两次。

但也可以避免。

class Solution{
public:TreeNode* invertTree(TreeNode* root){if(root == NULL)return root;invertTree(root->left);  //左swap(root->left,root->right); //中invertTree(root->left);//注意:这里仍然要遍历左孩子,因为中间节点已经翻转了return root;}
};

但这不算真正的递归中序遍历了。

但使用迭代方式统一写法的中序遍历是可以的。

class Solution{
public:
TreeNode* invertTree(TreeNode* root){stack<TreeNode*>st;if(root != NULL)st.push(root);while(!st.empty()){TreeNode* node = st.top();if(node != NULL){st.pop();if(node->right)st.push(node->right);//右st.push(node);//中st.push(NULL);if(node->left)st.push(node->left);//左}else{st.pop();node = st.top();st.pop();swap(node->left,node->right); //节点处理逻辑}}return root;
}
};

上面是用栈来遍历,而不是用指针,避免了递归法中翻转了两次的情况。

总结:对于二叉树的问题,解题前想清楚究竟是前中后序遍历,还是层序遍历。

对称二叉树

判断对称二叉树要比较的是哪两个节点,要比较的不是左右节点,而是两个树,这个树是根节点的左右子树,所以在递归遍历的过程中,也要同时遍历两棵树。

如何比较?比较两个子树的里侧和外侧元素是否相等。

遍历的顺序只能是“后序遍历”,因为我们要通过递归函数的返回值来判断两个子树的内测节点和外侧节点是否相等。

正是因为要遍历两棵树而且要比较内侧和外侧节点,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。

但都可以理解算是后序遍历,尽管已经不是严格上在一个树上进行遍历的后序遍历了。

后序可以理解为一种回溯

递归法

递归三部曲:

1.确定递归函数的参数和返回值

因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。

返回值自然是bool类型。

代码如下:

bool comppare(TreeNode* left,TreeNode* right)

2.确定终止条件

要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚!否则后面比较数值的时候就会操作空指针了。

节点为空的情况有:(注意!!!比较的是左右节点,是节点,不是左右孩子)

左节点为空,右节点不为空,不对称,return false

左不为空,右为空,不对称return false

左右都为空,对称,返回true

此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:

左右节点都不为空,比较节点数值,不相同就return false

此时左右节点不为空,且数值也不相同的情况我们也处理了。

代码如下:

if(left == NULL && right != NULL)return false;
else if(left != NULL && right == NULL)return false;
else if(left == NULL && right == NULL)return true;
else if(left->val != right->val)return false;

注意最后一种情况,没用else 而是else if,因为我们把以上情况都排除之后,剩下的就是左右节点都不为空,且数值相同的情况。

3.确定单层递归的逻辑

此时才进入单层递归的逻辑层递归的逻辑就是处理 左右节点都不为空,且数值相同的情

比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。

比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。

比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。

如果左右都对称就返回true,有一侧不对称就返回false.

bool outside = compare(left->left,right->right)//左子树:左、右子树;右
bool inside = compare(left->right,right->left);//左子树:右、右子树:左
bool isSame = outside && inside; //左子树:中、 右子树:中(逻辑处理)
retrun isSame;

如上代码中,我们可以看出使用的遍历方式,左子树左右中,右子树右左中,所以也叫做“后序遍历(尽管不是严格的后序遍历)。

class Solution{
public:bool compare(TreeNode* left,TreeNode* right){//首先排除空节点的情况if(left == NULL && right != NULL)return false;else if(left != NULL && right == NULL)return false;else if(left == NULL && right == NULL)return true;//排除了空节点,再排除数值不相同的情况else if (left->val != right->val)return false;//此时就是:左右节点都不为空,且数值相同的情况//此时才做递归,做下一层的判断bool outside = compare(left->left,right->right);//左子树:左、 右子树:右bool inside = compare(left->right,right->left);  //左子树:右、 右子树:左bool isSame = outside && inside;  //左子树:中、右子树:中(逻辑处理)return isSame;}bool isSymmetric(TreeNode* root){if(root == NULL)return true;return compare (root->left,root->right);}
};

迭代法

翻转二叉树也可以使用迭代法,但要注意,这里迭代法不是前中后序的迭代写法,因为本题的本质是判断两个树是否是相互翻转的,其实已经不是所谓二叉树遍历的前中后序的关系了。

这里我们可以使用队列来比较两个树(根节点左右子树)是否相互翻转,(注意这不是层序遍历)

使用队列

通过队列来判断根节点的左子树和右子树的内侧和外侧是否相等

class Solution{
public:bool isSymmetric(TreeNode* root){if(root == NULL)return true;queue<TreeNode*>que;que.push(root->left);//将左子树节点加入队列que.push(root->right);//将右子树头节点加入队列while(!que.empty()){//接下来就要判断这两个树是否相互翻转TreeNode* leftNode = que.front();que.pop();TreeNode* rightNode = que.front();que.pop();if(!leftNode && !rightNode){//左节点为空,右节点为空,此时说明是对称的continue;}//左右一个节点不为空,或者都不为空但数值不相同,返回falseif((!leftNode || rightNode || (leftNode->val != rightNode->val))){return false;}que.push(leftNode->left);//加入左节点左孩子que.push(rightNode->right);//加入右节点右孩子que.push(leftNode->right);//加入左节点右孩子que.push(rightNode->left);//加入右节点左孩子}return true;}
};

使用栈

把左右子树要比较的元素放进一个容器,然后成对的取出来进行比较,那么其实使用栈也是可以的。

只要把队列原封不动的改成栈就可以了

class Solution{
public:bool isSymmetric(TreeNode* root){if(root == NULL)return true;stack<TreeNode*>st;//这里改成了栈st.push(root->left);st.push(root->right);while(!st.empty()){TreeNode* leftNode = st.top();st.pop();TreeNode* rightNode = st.top();pop();if(!leftNode && !rightNode){continue;}if ((!leftNode || rightNode || (leftNode->val != rightNode -> val))){return false;}st.push(leftNode->left);st.push(rightNode->right);st.push(leftNode->right);st.oush(rightNode->left);}return true;}
};

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

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

相关文章

Task04:DDPG、TD3算法

本篇博客是本人参加Datawhale组队学习第四次任务的笔记 【教程地址】https://github.com/datawhalechina/joyrl-book 【强化学习库JoyRL】https://github.com/datawhalechina/joyrl/tree/main 【JoyRL开发周报】 https://datawhale.feishu.cn/docx/OM8fdsNl0o5omoxB5nXcyzsInGe…

Delphi 7 IdHTTP POST 中文乱码得解决

WEB后台使用UTF-8进行编码&#xff0c;由于D7默认是ansiString&#xff0c;直接提交到后台会使中文乱码。 解决方法&#xff1a; 1.先把AnsiString转WideString 2.通过System单元中的ansitoUTF8()函数进行转换之后再提交就可以了。 代码示例&#xff1a; procedure postDe…

华为三层交换机之基本操作

Telnet简介 Telnet是一个应用层协议,可以在Internet上或局域网上使用。它提供了基于文本的远程终端接口&#xff0c;允许用户在本地计算机上登录到远程计算机&#xff0c;然后像在本地计算机上一样使用远程计算机的资源。Telnet客户端和服务器之间的通信是通过Telnet协议进行的…

OpenAI 降低价格并修复拒绝工作的“懒惰”GPT-4,另外ChatGPT 新增了两个小功能

OpenAI降低了GPT-3.5 Turbo模型的API访问价格&#xff0c;输入和输出价格分别降低了50%和25%。这对于使用API进行文本密集型应用程序的用户来说是一个好消息。 OpenAI官网&#xff1a;OpenAI AIGC专区&#xff1a;aigc 教程专区&#xff1a;AI绘画&#xff0c;AI视频&#x…

Vite+Electron快速构建一个VUE3桌面应用(二)——动态模块热重载

一. 简介 在上一篇文章ViteElectron快速构建一个VUE3桌面应用中&#xff0c;我们了解了如何使用Vite和Electron来快速构建一个Vue3桌面应用。但是&#xff0c;之前构建的应用仅仅是一个简单的版本。在开发过程中&#xff0c;为了更好的开发体验&#xff0c;在开发electron的时…

博弈论(牛客练习赛)

思路&#xff1a;我们考虑小念赢 1、如果n>1并且p0&#xff0c;小念可以连续取两次&#xff0c;相当于小念有挂&#xff0c;可以从必败态转为必胜态&#xff0c;必赢。 2、如果n>1并且m>n-1&#xff0c;小念第一次取n-1个&#xff0c;小念必赢。 代码&#xff1a; …

【C语言】(1)初识C语言

什么是C语言 C语言是一种广泛应用的计算机编程语言&#xff0c;它具有强大的功能和灵活性&#xff0c;使其成为系统编程和底层开发的首选语言。C语言的设计简洁、高效&#xff0c;且不依赖于特定的硬件或系统&#xff0c;因此在各种计算平台上都能稳定运行。 C语言的特点 高…

C++ 之LeetCode刷题记录(十九)

&#x1f604;&#x1f60a;&#x1f606;&#x1f603;&#x1f604;&#x1f60a;&#x1f606;&#x1f603; 开始cpp刷题之旅。 依旧是追求耗时0s的一天。 108. 将有序数组转换为二叉搜索树 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你…

针对于vue element-plus组件的el-date-picker日期区间组件的日期格式问题以及如何进行区间判断

<template><el-date-picker v-model"value1" type"daterange" range-separator"To" start-placeholder"开始日期" end-placeholder"结束日期" :size"size" change"sarend" /> </templat…

速盾:海外网站CDN加速建站有哪些优势?

随着互联网的发展&#xff0c;海外网站的建设和访问需求也日益增加。而为了提高海外网站的访问速度和用户体验&#xff0c;CDN加速技术成为了一种必不可少的解决方案。本文将介绍海外网站CDN加速建站的优势&#xff0c;并回答一些相关问题。 一、提高网站访问速度 海外网站部署…

C#学习笔记_数组

数组是一个存储相同类型元素的固定大小的顺序集合。数组是用来存储数据的集合&#xff0c;通常认为数组是一个同一类型变量的集合。 声明数组 一、一维数组 一维数组声明语法如下&#xff1a; datatype[] arrayName; 其中&#xff0c;datatype为数据类型&#xff0c;array…

844.比较含退格的字符串(力扣LeetCode)

844.比较含退格的字符串 题目描述 给定 s 和 t 两个字符串&#xff0c;当它们分别被输入到空白的文本编辑器后&#xff0c;如果两者相等&#xff0c;返回 true 。# 代表退格字符。 注意&#xff1a;如果对空文本输入退格字符&#xff0c;文本继续为空。 示例 1&#xff1a;…

泽众云真机-机型集中化运维方案升级全面完成

2024年元月份&#xff0c;泽众云真机运维团队&#xff0c;经过几个月软硬件多轮安装调试&#xff0c;机型集中化运维方案升级全面完成。解决了云真机的机型集中化运维难题&#xff0c;方便了运营人员手机管理。 具体如下&#xff1a; 1、集中化运维&#xff0c;如服务器、PC、…

中级ccnp多久可以考下来?ccnp 课件哪里有?

思科中级CCNP考试要求考生具备一定的网络基础知识&#xff0c;包括IP地址规划、VLAN配置、STP协议等。然而&#xff0c;对于许多初学者来说&#xff0c;中级CCNP考试的难度和门槛都是一个不小的挑战。那么&#xff0c;究竟需要多久才能考下中级CCNP证书呢?下面将为你详细解读。…

esp32 操作DS1307时钟芯片

电气参数摘要 有VCC供电&#xff0c;IIC活动状态是1.5mA&#xff0c;待机状态200μA&#xff0c;电池电流5nA(MAX50nA&#xff09;无VCC供电的时候&#xff0c;电池电流&#xff0c;300nA&#xff08;时钟运行&#xff09;&#xff0c;10nA&#xff08;时钟停止&#xff09;供…

Compose中添加Android原生控件

文章目录 一、前言二、示例代码三、AndroidView的注意事项四、参考链接 一、前言 Compose自身组件有时候并不能完全满足要求&#xff0c;这里演示如何在Compose中添加原生组件及其注意事项 二、示例代码 Composablefun AndroidTextView(modifier: Modifier) {AndroidView(mod…

机器学习-pandas(含数据)

pandas 优势&#xff1a; 增强图表可读性便捷的数据处理能力读取文件方便封装了Matplotlib、Numpy的画图和计算 更详细的教程&#xff1a;Pandas 教程 | 菜鸟教程 (runoob.com) Pandas数据结构 Pandas中一共有三种数据结构&#xff0c;分别为&#xff1a;Series、DataFram…

SpringMVC-拦截器

文章目录 拦截器一、SpringMVC配置文件二、三个抽象方法 拦截器 一、SpringMVC配置文件 <!-- 配置拦截器--><mvc:interceptors><mvc:interceptor><mvc:mapping path"/**"/> <!---拦截的路径--><mvc:exclude-mapping path&qu…

Vite+Electron快速构建一个VUE3桌面应用(一)

一. 简介 首先&#xff0c;介绍下vite和Electron。 Vite是一种新型前端构建工具&#xff0c;能够显著提升前端开发体验。Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入Chromium和Node.js到二进制的 Electron 允许您保持一个 JavaScript 代码代码…

Docker 容器内运行 mysqldump 命令来导出 MySQL 数据库,自动化备份

备份容器数据库命令&#xff1a; docker exec 容器名称或ID mysqldump -u用户名 -p密码 数据库名称 > 导出文件.sql请替换以下占位符&#xff1a; 容器名称或ID&#xff1a;您的 MySQL 容器的名称或ID。用户名&#xff1a;您的 MySQL 用户名。密码&#xff1a;您的 MySQL …