二叉树的前序、中序、后序、层序遍历

参考内容:

五分钟让你彻底理解二叉树的非递归遍历

Python实现二叉树的非递归遍历

二叉树遍历——深度优先(前中后序)+广度优先(层序遍历)

构造二叉树

定义二叉树结构如下

struct node
{int data;node *left;node *right;
};

构造如下形态二叉树

node *init_tree()
{node *node1 = (node *)malloc(sizeof(node));node *node2 = (node *)malloc(sizeof(node));node *node3 = (node *)malloc(sizeof(node));node *node4 = (node *)malloc(sizeof(node));node *node5 = (node *)malloc(sizeof(node));node *node6 = (node *)malloc(sizeof(node));node *node7 = (node *)malloc(sizeof(node));node *node8 = (node *)malloc(sizeof(node));node1->data = 1;node2->data = 2;node3->data = 3;node4->data = 4;node5->data = 5;node6->data = 6;node7->data = 7;node8->data = 8;node1->left = node2;node1->right = node3;node2->left = node4;node2->right = node5;node3->right = node6;node5->left = node7;node5->right= node8;return node1;
}

前序遍历(递归)

前序遍历顺序为根左右。要遍历整个二叉树我们就需要遍历二叉树的每一个子树,对于任何一个子树它的遍历方式均为根左右顺序遍历。即所有子问题均与父问题除规模大小不同外,其余均相同。所以可以采用递归方式实现前序遍历。

// 前序遍历 根左右
void pre_order_traversal(node *root)
{if(root) {cout<<root->data<<" ";pre_order_traversal(root->left);pre_order_traversal(root->right);}
}

遍历结果为:1 2 4 5 7 8 3 6

中序遍历(递归)

中序遍历顺序为左根右。其与前序遍历仅顺序不同,其余均相同。

// 中序遍历 左根右
void in_order_traversal(node *root)
{if(root) {in_order_traversal(root->left);cout<<root->data<<" ";in_order_traversal(root->right);}
}

遍历结果为:4 2 7 5 8 1 3 6

后序遍历(递归)

后序遍历顺序为左右根。其与前序、中序遍历仅顺序不同,其余均相同。

// 后序遍历 左右根
void post_order_traversal(node *root)
{if(root) {post_order_traversal(root->left);post_order_traversal(root->right);cout<<root->data<<" ";}
}

遍历结果为:4 7 8 5 2 6 3 1

前序遍历方法一(非递归)

因为递归实际上是由系统帮我们进行压栈,所以理论上所有递归算法都可以改为循环+栈实现,那么我们先照着上述前序遍历的样子修改为循环+栈的形态。需要注意的是由于栈先进后出的特性,为了保证左孩子在右孩子前被访问,所以应该先右孩子入栈,再左孩子入栈。

// 前序遍历 根左右
void pre_order_traversal(node *root)
{stack<node *> s;s.push(root);while(!s.empty()) {node *cur = s.top();s.pop();if(cur) {cout<<cur->data<<" ";s.push(cur->right);s.push(cur->left);}}
}

遍历结果为:1 2 4 5 7 8 3 6

前序遍历方法二(非递归)

现在我们换一种思路来实现前序非递归遍历,仔细观察前序遍历的递归调用过程。

  1. 先把从根结点开始的所有左子树放入栈中;
  2. 弹出栈顶元素
  3. 如果栈顶元素有右子树,那么右子树入栈
  4. 重复上述过程直到栈为空

因此我们可以写出遍历代码

// 前序遍历 根左右
void pre_order_traversal(node *root)
{stack<node *> s;node *cur = root;while(cur || !s.empty()) {// 将左子树全部入栈while(cur) {cout<<cur->data<<" ";s.push(cur);cur = cur->left;}if(!s.empty()) {cur = s.top();s.pop();cur = cur->right;}}
}

遍历结果为:1 2 4 5 7 8 3 6

中序遍历(非递归)

有了前面的基础,我们再来考虑中序遍历,会发现中序遍历与前序遍历只是打印结点的位置不一样。前序遍历是在结点入栈时打印,中序遍历只需要替换为在结点出栈时打印即可。

// 中序遍历 左根右
void in_order_traversal(node *root)
{stack<node *> s;node *cur = root;while(cur || !s.empty()) {// 将左子树全部入栈while(cur) {s.push(cur);cur = cur->left;}if(!s.empty()) {cur = s.top();cout<<cur->data<<" ";s.pop();cur = cur->right;}}
}

遍历结果为:4 2 7 5 8 1 3 6

后序遍历方法一(非递归)

后序遍历相对来说显得更加复杂了。在前序和中序遍历中,只要左子树处理完毕实际上栈顶元素就可以出栈了,但后序遍历需要把左子树和右子树都处理完毕才能出栈,显然我们需要某种方法记录遍历的过程。

实际上我们只需要记录下遍历的前一个结点就能解决问题,因为通过前一个结点我们可以做如下判断:

  1. 如果前一个结点是当前结点的右子树,那么说明右子树已经遍历完毕可以出栈了
  2. 如果前一个结点是当前结点的左子树而且当前结点没有右子树,那么说明可以出栈了
  3. 如果当前结点即没有左子树也没有右子树,即为叶子结点,那么说明可以出栈了

若不属于上述情况,则依次将当前结点的右孩子和做孩子入栈,这样就能保证每次取栈顶元素时,左孩子都在右孩子前面被访问,左孩子和右孩子都在父结点前面被访问。

// 后序遍历 左右根
void post_order_traversal(node *root)
{stack<node *> s;node *pre = NULL;node *cur = root;s.push(cur);while(!s.empty()) {cur = s.top();// 叶子结点if((!cur->left && !cur->right) // 叶子结点|| pre == cur->right // 前一个结点为当前结点右子树|| (pre == cur->left && !cur->right)) { // 前一个结点为当前结点左子树,且没有右子树cout<<cur->data<<" ";pre = cur;s.pop();} else {if(cur->right)s.push(cur->right);if(cur->left)s.push(cur->left);}}
}

遍历结果为:4 7 8 5 2 6 3 1

后序遍历方法二(非递归)

后序遍历的顺序是左右根,如果把这个顺序倒过来就是根右左,是不是发现和前序遍历很像?那么我只需要按照根右左的方式遍历完,然后将遍历结果掉一个个儿就可以,而栈就具备掉个儿的功能,因此可写出如下代码。

// 后序遍历 左右根
void post_order_traversal(node *root)
{stack<node *> s;stack<int> ans;node *cur = root;while(cur || !s.empty()) {// 将左子树全部入栈while(cur) {ans.push(cur->data);s.push(cur);cur = cur->right;}if(!s.empty()) {cur = s.top();s.pop();cur = cur->left;}}while(!ans.empty()) {cout<<ans.top()<<" ";ans.pop();}
}

遍历结果为:4 7 8 5 2 6 3 1

层序遍历

层序遍历即广度优先遍历,使用队列即可实现。

// 层序遍历
void breadth_first_order_traversal(node *root)
{queue<node *> q;q.push(root);while(!q.empty()){node *cur = q.front();q.pop();if(cur){cout<<cur->data<<" ";q.push(cur->left);q.push(cur->right);}}
}

遍历结果为:1 2 3 4 5 6 7 8

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

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

相关文章

C++前缀和算法的应用:统计上升四元组

C前缀和算法的应用&#xff1a;统计上升四元组 本文涉及的基础知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 题目 给你一个长度为 n 下标从 0 开始的整数数组 nums &#xff0c;它包含 1 到 n 的所有数字&#xff0c;请你返回上…

关于网站安全的一些讨论

互联网的普及和发展为企业和个人提供了巨大的机会&#xff0c;但同时也伴随着网络安全威胁的增加。网站被攻击是一个常见的问题&#xff0c;可能导致数据泄露、服务中断和声誉受损。在本文中&#xff0c;我们将探讨与网络安全紧密相关的因素&#xff0c;分析为什么网站容易受到…

Si4010 一款带有MCU SoC RF发射机芯片 无线遥控器

Si4010是一款完全集成的SoC RF发射机&#xff0c;带有嵌入式CIP-51 8051 MCU&#xff0c;专为1GHz以下ISM频带设计。该芯片针对电池供电的应用进行了优化&#xff0c;工作电压为1.8至3.6 V&#xff0c;待机电流小于10 nA的超低电流消耗。高功率放大器可提供高达10 dBm的输出功率…

Linux Crontab 定时任务

crond 服务 Linux 通过 crond 服务来支持 crontab。 查看 crond 服务是否已经安装 输入下面命令确认 crond 服务是否已安装。 systemctl list-unit-files | grep crond 如果为 enabled&#xff0c;表示服务正运行。 crontab 文件 crontab 要执行的定时任务都被保存在 /etc…

seata1.8安装部署

1.在nacos里面创建命名空间 2.下载seata安装包 3.将下载的seata解压&#xff0c;找到seata/script/server/db目录下对应数据库的sql脚本&#xff0c;创建数据库 undo_log.sql CREATE TABLE undo_log (branch_id bigint(20) NOT NULL COMMENT branch transaction id,xid varcha…

3线SPI驱动 HX8347 TFT屏

老五家2.8寸屏&#xff0c;3线SPI驱动 前言 要知道屏幕的驱动芯片都小的惊人&#xff0c;想必是不会打上丝印的。从几百个引脚中判断哪个是哪个&#xff0c;想想就晕。 大佬们都太厉害了&#xff0c;看看PFC就知道屏幕的接线定义。一直好奇这种神技是怎么练成的。也尝试自己来…

字符型液晶显示器LCD 1602的显示控制(Keil+Proteus)

前言 趁机把LCD 1602的实验完成了&#xff0c;那个电路图有几个地方没弄懂&#xff0c;但是去掉也没有报错&#xff0c;就没管了。 LCD1602_百度百科 (baidu.com)https://baike.baidu.com/item/LCD1602/6014393?frge_ala LCD1602液晶显示屏通过电压来改变填充在两块平行板之…

动态规划专题——背包问题

&#x1f9d1;‍&#x1f4bb; 文章作者&#xff1a;Iareges &#x1f517; 博客主页&#xff1a;https://blog.csdn.net/raelum ⚠️ 转载请注明出处 目录 前言一、01背包1.1 使用滚动数组优化 二、完全背包2.1 使用滚动数组优化 三、多重背包3.1 使用二进制优化 四、分组背包…

混合云中 DevOps 的最佳实践

近年来&#xff0c;出现了各种工具、技术和框架&#xff0c;其目标是增强灵活性、性能和可扩展性。传统的整体方法已被微服务和纳米服务等更加模块化的方法所取代。此外&#xff0c;云计算的兴起导致本地软件被云环境所取代&#xff0c;云环境提供了以前无法提供的广泛优势和功…

Qwt QwtThermo绘制温度计

1.简介 QwtThermo 是一个基于 Qt 框架的类库&#xff0c;用于创建温度计控件。它提供了一些方便的功能来展示和处理温度计相关的数据。 QwtThermo 添加了特定于温度计的功能。 使用 QwtThermo&#xff0c;可以实现以下功能&#xff1a; 设置温度范围&#xff1a;可以通过设置…

【EI会议征稿】第四届智慧城市工程与公共交通国际学术会议(SCEPT 2024)

第四届智慧城市工程与公共交通国际学术会议&#xff08;SCEPT 2024&#xff09; 2024 4th International Conference on Smart City Engineering and Public Transportation 第四届智慧城市工程与公共交通国际学术会议&#xff08;SCEPT 2024&#xff09;将于2024年1月26-28日…

折叠旗舰新战局:华为先行,OPPO接棒

乌云中的曙光&#xff0c;总能带给人希望。 全球智能手机出货量已经连续八个季度下滑&#xff0c;行业里的乌云挥之不散。不过&#xff0c;也能看到高端市场逆势上涨&#xff0c;散发光亮。个中逻辑在于&#xff0c;当前换机周期已经达到了34个月&#xff0c;只有创新产品才能…

【ARFoundation学习笔记】平面检测

写在前面的话 本系列笔记旨在记录作者在学习Unity中的AR开发过程中需要记录的问题和知识点。难免出现纰漏&#xff0c;更多详细内容请阅读原文。 文章目录 平面检测属性可视化平面平面检测的开关控制显示与隐藏已检测平面 平面检测属性 AR中检测平面的原理&#xff1a;AR Fou…

洛谷P1024 [NOIP2001 提高组] 一元三次方程求解(优雅的暴力+二分,干净利落)

P1024 [NOIP2001 提高组] 一元三次方程求解 前言题目题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 题目分析注意事项 代码后话额外测试用例样例输入 #2样例输出 #2 王婆卖瓜 题目来源 前言 没有前言&#xff0c;可能因为作者忘了编辑 题目 题目描述 有形如&…

使用Redis实现缓存及对应问题解决

一、为什么需要Redis作缓存&#xff1f; 在业务场景中&#xff0c;如果有些数据需要极高频的存取&#xff0c;每次都要在mysql中查询的话代价太大&#xff0c;假如有一个存在于客户端和mysql之间的存储空间&#xff0c;每次可以在这空间中进行存取操作&#xff0c;就会减轻mys…

简单工厂模式、工厂方法模式、抽象工厂模式

简介 将实例化代码提取出来&#xff0c;放到一个类中统一管理和维护&#xff0c;达到和主项目依赖关系的解耦&#xff0c;从而提高项目的扩展性和维护性。 工厂模式将复杂的对象创建工作隐藏起来&#xff0c;而仅仅暴露出一个接口供客户使用&#xff0c;具体的创建工作由工厂管…

跨境电商平台Etsy被封?那是因为你没做对这件事!

ETSY是一个在线市场和电商平台&#xff0c;专注于手工艺品、独特商品和创意艺术。它为卖家提供了一个平台来展示和销售自己的手工制品、艺术品、珠宝、家居用品、时尚配饰等各种创意产品。作为一个颇受中国商家青睐的平台&#xff0c;Etsy在账号检测方面也是不亚于亚马逊之严格…

设计模式之工厂模式(Factory)

任何可以产生对象的方法或类&#xff0c;都可以称为工厂。 下面的代码定义了Car这种交通工具: public class Car {public void go() {System.out.println("Car go wuwuwuwuw....");} }然后在main函数里面想要调用调用Car的go方法&#xff0c;就需要new一个car对象&…

音频修复增强软件iZotope RX 10 mac中文特点

iZotope RX 10 mac是一款音频修复和增强软件。 iZotope RX 10 mac主要特点 声音修复&#xff1a;iZotope RX 10可以去除不良噪音、杂音、吱吱声等&#xff0c;使音频变得更加清晰干净。 音频增强&#xff1a;iZotope RX 10支持对音频进行音量调节、均衡器、压缩器、限制器等处…

grdle 的安装与配置 、gradle和jdk版本对应关系

java与gradle对应的版本关系 Java Java Gradle需要Java版本在8到19之间。目前还不支持Java 20及更高版本。 Java 6和Java 7仍然可以用于编译&#xff0c;但已经不适合用于测试。Gradle 9.0不支持Java 6和Java 7的测试。任何完全支持的Java版本都可以用于编译或测试。 然而&…