代码随想录笔记|C++数据结构与算法学习笔记-二叉树(一)|二叉树的递归遍历、二叉树的迭代遍历、二叉树的统一迭代法

全文基于代码随想录及相关讲解视频。
文字链接:《代码随想录》

文章目录

  • 二叉树的递归遍历
    • 二叉树的前序遍历
      • C++代码如下
    • 二叉树的中序遍历
    • 二叉树的后序遍历
  • 二叉树的迭代遍历
    • 前序遍历
      • 前序遍历C++代码
    • 右序遍历
      • 右序遍历C++代码
    • 中序遍历
      • 为什么中序遍历不同
      • 中序遍历迭代法的过程
      • 中序遍历C++代码
  • 二叉树的统一迭代法
    • 中序遍历
    • 前序遍历
    • 后序遍历

二叉树的递归三部曲:(非常重要,每一题几乎都要拿来分析)

  • 确定递归函数的参数和返回值
  • 确定终止条件
  • 确定单层递归的逻辑

二叉树的递归遍历

  • 144.二叉树的前序遍历
  • 145.二叉树的后序遍历
  • 94.二叉树的中序遍历

二叉树的前序遍历

前序是中左右的遍历顺序

  • 确定递归函数的参数和返回值:

​ 参数并不是要一次性确定的,在写递归函数的过程中我们需要什么样的参数,再传入什么样的参数即可,不过大多数二叉树的题目所需要的参数并不多,基本就是传入一个根结点,在传入一个数组来放我们遍历的结果返回值一般都是void,因为我们需要的结果已经放入到传入参数的数组里了

​ 就本题来说void traversal(TreeNode* cur, vector<int>& vec)

  • 确定终止条件:

​ 确定终止条件对于递归来说也非常重要,对于一个前序遍历,当他猛猛搜索一直搜索到null那肯定就往上返。

if (cur == NULL) return;

  • 确定单层递归的逻辑

​ 单层递归逻辑其实就是在递归函数中要写的东西,这里我们实现的中左右,所以我们要处理的结点首先是中间节点。所以数组要放我们遍历过的元素vec.push(cur->val);什么是左呢,就是向左遍历traversal(cur->left, val);右就是遍历它的右孩子traversal(cur->right, vec);这样我们就实现了中序遍历。

综上伪代码如下:

void traversal(TreeNode* cur, vector<int>& vec)if (cur == NULL) return;//中vec.push(cur->val)//左traversal(cur->left, val)//右traversal(cur->right, vec)

写前序、中序、后序代码的原则跟我们讲的前序的顺序一致:

中序:

	//左traversal(cur->left, val)//中vec.push(cur->val)//右traversal(cur->right, vec)

后序同理。

C++代码如下

class Solution {
public:void traversal(TreeNode* cur, vector<int>& vec) {if (cur == NULL) return;vec.push_back(cur->val);    // 中traversal(cur->left, vec);  // 左traversal(cur->right, vec); // 右}vector<int> preorderTraversal(TreeNode* root) {vector<int> result;traversal(root, result);return result;}
};

二叉树的中序遍历

void traversal(TreeNode* cur, vector<int>& vec) {if (cur == NULL) return;traversal(cur->left, vec);  // 左vec.push_back(cur->val);    // 中traversal(cur->right, vec); // 右
}

二叉树的后序遍历

void traversal(TreeNode* cur, vector<int>& vec) {if (cur == NULL) return;traversal(cur->left, vec);  // 左traversal(cur->right, vec); // 右vec.push_back(cur->val);    // 中
}

二叉树的迭代遍历

看完本篇大家可以使用迭代法,再重新解决如下三道leetcode上的题目:

  • 144.二叉树的前序遍历
  • 94.二叉树的中序遍历
  • 145.二叉树的后序遍历

文章链接:非递归遍历

视频链接:写出二叉树的非递归遍历很难么?(前序和后序)

写出二叉树的非递归遍历很难么?(中序)

前序遍历

​ A
/ \
B C
/ \
D E

对于以上的二叉树,遍历顺序是ABDEC。编程语言实现递归其实就是用栈来实现的,所以我们这里用迭代来模拟递归,也是用栈这种结构。理论上来讲所有递归都能用栈来模拟。

栈底[ ]栈顶 栈入作所示,先把A入栈,然后把A弹出。为什么要弹出呢,因为我们要把A放入数组里了,因为A是我们要处理的元素。A弹出之后,我们要处理A的左右孩子。然后依次把C、B入栈。此时:栈底[C, B]栈顶。本来我们要拿到B,但是先把C入栈,这是因为栈是先进后出的,这样我们就能先拿B,再拿C,实现中左右的遍历顺序。然后我们弹出B放入数组,现在数组是[A, B]。后面对于B的子树的处理同理。具体可以看上文视频。

再一个就是碰到叶子结点就不用处理它的左右孩子了。

详细代码如下:

前序遍历C++代码

class Solution {
public:vector<int> preorderTraversal(TreeNode* root) {stack<TreeNode*> st;vector<int> result;if (root == NULL) return result;st.push(root);while (!st.empty()) {TreeNode* node = st.top();                       // 中st.pop();result.push_back(node->val);if (node->right) st.push(node->right);           // 右(空节点不入栈)if (node->left) st.push(node->left);             // 左(空节点不入栈)}return result;}
};

右序遍历

20200808200338924

我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了

右序遍历C++代码

class Solution {
public:vector<int> postorderTraversal(TreeNode* root) {stack<TreeNode*> st;vector<int> result;if (root == NULL) return result;st.push(root);while (!st.empty()) {TreeNode* node = st.top();st.pop();result.push_back(node->val);if (node->left) st.push(node->left); // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)if (node->right) st.push(node->right); // 空节点不入栈}reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了return result;}
};

中序遍历

为什么中序遍历不同

之前代码有两个重点,一个是访问结点,一个是处理结点。访问结点就是在二叉树中从根结点开始一个结点一个结点开始访问;处理结点就是结点元素保存到数组中,这个数组就是我们要输出的顺序。

如果是中序遍历,对于上文的二叉树:

​ A

/
B C
/ \
D E

我们还是要先访问根结点,但是我们先处理的却不能是根结点,中序遍历的第一个元素应该是D。这就造成了我们访问的顺序和我们处理的顺序不一样。这就是为什么我们的中序遍历是另外一套写法。

中序遍历迭代法的过程

中序遍历为:DBEAC

首先还是要用栈来记录我们遍历过的顺序,因为我们处理元素的时候其实按遍历顺序逆向输出的

我们先访问A,然后把A入栈,然后访问B把B入栈,然后是D,把D入栈。此时我们到叶子结点了(怎么知道到叶子结点了呢?因为我们再访问D的左孩子是空,所以肯定是叶子结点)。也就是因为D的左孩子为空,我们从栈取元素,就是D,D就是我们要处理的第一个元素。此时把D看成一个独立的二叉树。D是中,左右都是空,所以第一个输出的元素是D,把D加入到数组里。

然后由于D右也是空,我们就把B弹出来加入到数组。

然后B的右孩子不为空,栈记录我们遍历的元素E,然后指针还是往E的左孩子走,又是空!所以我们就把E弹出。

随后看E的右孩子,也是空!,所以我们就再从栈内弹出元素A加入到数组中。

然后就找A的右孩子C加入到数组中。然后C的左孩子为空,所以把C弹出加入数组,再看C的右孩子,也为空,理论上我们应该从栈内弹出元素,但是栈也为空了,所以我们的遍历流程就结束了。

综上,数组中的顺序为DBEAC,与答案一致

伪代码如下:

 vector<int> traversal (root){vector<int> result;stack<TreeNode*> st;Treenode* cur = root; //当前结点指向根结点while (cur != NUll || !st.empty()){if (cur != Null)	//如果当前元素不为空{st.push(cur);	//吧当前方位的元素加入到栈里 cur = cur->left; //当前结点往左走}else{cur = st.top(); st.pop();result.push_back(cur->val);cur = cur->right;}return result;   }}

中序遍历C++代码

class Solution {
public:vector<int> inorderTraversal(TreeNode* root) {vector<int> result;stack<TreeNode*> st;TreeNode* cur = root;while (cur != NULL || !st.empty()) {if (cur != NULL) { // 指针来访问节点,访问到最底层st.push(cur); // 将访问的节点放进栈cur = cur->left;                // 左} else {cur = st.top(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)st.pop();result.push_back(cur->val);     // 中cur = cur->right;               // 右}}return result;}
};

二叉树的统一迭代法

主要思想就是利用一个栈来完成遍历结点和处理结点的过程。

统一风格代码如下:

中序遍历

class Solution {
public:vector<int> inorderTraversal(TreeNode* root) {vector<int> result;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();result.push_back(node->val); // 加入到结果集}}return result;}
};

前序遍历

class Solution {
public:vector<int> preorderTraversal(TreeNode* root) {vector<int> result;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);  // 右if (node->left) st.push(node->left);    // 左st.push(node);                          // 中st.push(NULL);} else {st.pop();node = st.top();st.pop();result.push_back(node->val);}}return result;}
};

后序遍历

class Solution {
public:vector<int> postorderTraversal(TreeNode* root) {vector<int> result;stack<TreeNode*> st;if (root != NULL) st.push(root);while (!st.empty()) {TreeNode* node = st.top();if (node != NULL) {st.pop();st.push(node);                          // 中st.push(NULL);if (node->right) st.push(node->right);  // 右if (node->left) st.push(node->left);    // 左} else {st.pop();node = st.top();st.pop();result.push_back(node->val);}}return result;}
};

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

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

相关文章

FEX-Emu在Debian/Ubuntu系统使用

FEX-Emu在Debian/Ubuntu系统使用 1. Debootstrap子系统安装&#xff08;可选&#xff09;2. Debian/Ubuntu依赖包安装3. 获取FEX-Emu源码并编译4. 根文件系统RootFS安装5. 基于 FEX-Emu 运行应用 1. Debootstrap子系统安装&#xff08;可选&#xff09; sudo apt-get install …

如何让电脑定时开机?这个方法你一定要学会

前言 前段时间小白在上班的时候&#xff0c;个人使用一台台式机和一台笔记本电脑。台式机并不是经常使用&#xff0c;但整个公司的数据中心是建立在小白所使用的那台台式机上。 如果台式机没有开机&#xff0c;同事们就没办法访问数据中心获取自己想要的资料。领导也没办法链…

C#、.NET版本、Visual Studio版本对应关系及Visual Studio老版本离线包下载地址

0、写这篇文章的目的 由于电脑的环境不同&#xff0c;对于一个老电脑找到一个适配的vscode环境十分不易。总结一下C#、.NET、Visual Studio版本的对应关系&#xff0c;及各个版本Visual Studio的下载地址供大家参考 1、C#、.NET版本、Visual Studio版本对应关系如下 2、Visua…

李国武:如何评估一家精益制造咨询公司的实施能力?

在制造业转型升级的大背景下&#xff0c;精益制造已成为企业提升竞争力、实现可持续发展的关键。然而&#xff0c;面对市场上众多的精益制造咨询公司&#xff0c;如何评估其实施能力成为了众多企业的难题。本文将从多个方面为大家揭示评估精益制造咨询公司实施能力的方法&#…

MySQL数据库的索引

目录 1、索引的概念 2、索引的作用 优点 缺点 3、创建索引的原则依据 4、索引的分类和创建 ​编辑 4.1普通索引 直接创建索引 修改表方式创建索引 创建表时添加索引 删除索引 4.2唯一索引 直接创建唯一索引 修改表方式创建 创建表时指定索引 4.3主键索引&…

Lamdba表达式

Lamdba表达式 Lambda是一个匿名函数&#xff0c;我们可以将Lambda表达式理解为一段可以传递的代码&#xff08;将代码像数据一样 传递&#xff09;。使用它可以写出简洁、灵活的代码。作为一种更紧凑的代码风格&#xff0c;使java语言表达能力得到提 升。 Lambda表达式在java语…

Android Kotlin(六)协程的并发问题

书接上回&#xff1a;Android Kotlin知识汇总&#xff08;三&#xff09;Kotlin 协程 协程的并发问题 在一个协程中&#xff0c;循环创建10个子协程且单独运行各自Default线程中&#xff0c;并让每个子协程对变量 i 进行1000次自增操作。示例如下&#xff1a; fun main() …

Golang基础知识(笔记迁移)

golang 变量作用域 局部作用域&#xff1a;代码块、函数内的全局作用域&#xff1a;顶层作用域&#xff0c;代码块外的就是全局&#xff0c;如果变量名大写&#xff0c;则改变量整个程序都可以使用。 类型断言 golang的类型断言在变量后加上.(type)&#xff0c;如果类型断言…

脚本实现Ubuntu设置屏幕无人操作,自动黑屏

使用 xrandr 命令可以实现对屏幕的控制&#xff0c;包括调整分辨率、旋转屏幕以及关闭屏幕等。要实现 Ubuntu 设置屏幕在无人操作一段时间后自动黑屏&#xff0c;非待机&#xff0c;并黑屏后点击触摸屏可以唤醒屏幕&#xff0c;可以借助 xrandr 命令来实现。 首先&#xff0c;…

比一比gitee、gitlab、github

gitee、gitlab、github&#xff0c;哪个是目前国内大型公司使用最多的呢&#xff1f;共同点&#xff1a;三者都是基于git的代码托管工具&#xff0c;都支持版本管理。 gitee&#xff1a;适合国内开发者&#xff0c;更友好的本地化服务&#xff0c;形成了一个适合中国宝宝学习的…

计算机网络:分层体系结构

计算机网络&#xff1a;分层体系结构 基本分层概述各层次的任务物理层数据链路层网络层运输层应用层 数据传递过程分层体系常见概念实体协议服务 基本分层概述 为了使不同体系结构的计算机网络都能互联&#xff0c;国际标准化组织于 1977 年成立了专门机构研究该问题。不久他们…

【C++】1597. 买文具

问题&#xff1a;1597. 买文具 类型&#xff1a;基本运算、整数运算 题目描述&#xff1a; 花花去文具店买了 1 支笔和 1块橡皮&#xff0c;已知笔 x 元/ 支&#xff0c;橡皮 y元 / 块&#xff0c;花花付给了老板 n 元&#xff0c;请问老板应该找给花花多少钱&#xff1f; 输…

pycharm安装插件

pycharm安装插件显示网络错误/无法安装 输入以下网址 https://plugins.jetbrains.com/ 然后重启pycharm就可以安装软件了

Spring Cloud Alibaba Sentinel 使用详解

一、Sentinel 介绍 随着微服务的流行&#xff0c;服务和服务之间的稳定性变得越来越重要。 Sentinel 以流量为切入点&#xff0c;从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 Sentinel 具有以下特征: 丰富的应用场景&#xff1a; Sentinel 承接了阿里巴…

现代游戏引擎架构

一、并行编程 1.1 为什么需要并行编程 游戏的渲染计算对算力要求很高&#xff0c;所以我们需要把操作系统的资源利用到极致。 但是摩尔定律已经不在适用了&#xff0c;硬件的发展目前已经达到瓶颈。所以我们需要通过数量来提高计算效率。 1.2 并行编程基础 进程与线程&#…

sqlite3 交叉编译

#1.下载源码并解压 源码路径如下&#xff0c;下载autoconf版本 SQLite Download Page 解压 tar -zxvf sqlite-autoconf-3450200.tar.gz cd sqlite-autoconf-3450200 mkdir build # 2. 配置源代码 # 假设你已经安装了交叉编译工具链&#xff0c;如gcc-arm-linux-gnueabih…

Tkinter 一文读懂

Tkinter 简介 Tkinter&#xff08;即 tk interface&#xff0c;简称“Tk”&#xff09;本质上是对 Tcl/Tk 软件包的 Python 接口封装&#xff0c;它是 Python 官方推荐的 GUI 工具包&#xff0c;属于 Python 自带的标准库模块&#xff0c;当您安装好 Python 后&#xff0c;就可…

基于python+vue的BBS论坛系统flask-django-nodejs-php

本系统为用户而设计制作BBS论坛系统&#xff0c;旨在实现BBS论坛智能化、现代化管理。本BBS论坛自动化系统的开发和研制的最终目的是将BBS论坛的运作模式从手工记录数据转变为网络信息查询管理&#xff0c;从而为现代管理人员的使用提供更多的便利和条件。使BBS论坛系统数字化、…

关于短群签名论文阅读

参考文献为2004年发表的Short Group Signatures 什么群签名&#xff1f; 群签名大致就是由一组用户组成一个群&#xff0c;其中用户对某条消息的签名&#xff0c;改签名不会揭示是哪一个用户签署的&#xff0c;签名只能表明该消息确实是来自该群的签名。对于群还有一个群管理者…

Spring Boot 3 极速搭建OAuth2认证框架

本篇环境 Java 17Spring Boot 3.2.3Spring Authorization Server 1.2.3开发工具 SpringToolSuite4Spring Boot 3.2.3 需要JDK 17及之上的版本。 项目初始化 项目可以使用Spring的初始化器生成, 也可以创建一个Maven类型的项目。 项目创建后的目录结构如下: 项目配置 使用 …