【算法与数据结构】二叉树的三种遍历代码实现(下)—— 非递归方式实现(大量图解)

news2024/9/22 1:08:00

 上篇:【算法与数据结构】二叉树的三种遍历代码实现(上)—— 用递归序知识点讲解_Hacynn的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/zzzzzhxxx/article/details/133609612?spm=1001.2014.3001.5502

目录

前言

1、先序遍历

1.1、详细图解描述

1.2、先序遍历非递归代码实现 

2、中序遍历

2.1、详细图解描述 

2.2、中序遍历非递归代码实现  

3、后序遍历

3.1、详细图解描述 

3.2、后序遍历非递归代码实现   


前言

        在上篇当中给大家介绍了二叉树的先序遍历、中序遍历以及后序遍历的递归写法。递归的系写法主要是理解递归序,只要递归序能够理解清楚,就能够很轻易地理解和书写递归实现三次遍历。

        任何递归函数都可以改成非递归函数,因为递归函数不是什么玄学,只是递归时系统帮忙解决了压栈问题。那么不用递归方式的话只要自己手动进行压栈依然可以完成递归能够实现的功能。

        那么在接下来的下篇中,我将带大家审深入学习二叉树三种遍历的非递归写法,也是二叉树遍历的代码中的重点内容,因为递归方式的代码非常好实现,因此在面试时常常会考大家的是非递归的写法。

1、先序遍历

先说结论:首先准备一个栈,将头结点入栈后,执行循环操作:

  1. 从栈中弹出一个结点记为cur。
  2. 对cur进行打印(或者是处理)操作。
  3. 如果cur有右孩子的话,则将cur的右孩子入栈。
  4. 如果cur有左孩子的话,则将cur的左孩子入栈。(即先右再左)注意区分此时的入栈顺序和递归实现遍历时的打印顺序的不同,不要搞混淆。
  5. 周而复始。

压栈顺序:头右左

1.1、详细图解描述

下面进行详细步骤描述: 

 首先先把头结点进栈,即1结点入栈。

从栈中弹出一个结点记为cur,此时的cur即为1。对cur进行打印(或者其他处理)操作,如果cur有孩子的话,则将cur的右节点和左节点按右左顺序入栈。即此时 结点3结点2 入栈。

周而复始,再次从栈中弹出一个结点记为cur,此时的cur即为2。对cur进行打印(或者其他处理)操作,并将cur的孩子按右左顺序入栈。即此时 结点5结点4 入栈。

周而复始,栈中弹出结点4记为cur,打印cur,并压栈cur左右孩子结点,但是此时cur(也就是4结点)并没有左右孩子结点了,所以不进行压栈操作。

 周而复始,栈中弹出结点5记为cur,打印cur,5和4一样没有孩子节点,所以不进行压栈。

周而复始,栈中弹出结点3记为cur,打印cur,将孩子结点6和7入栈。 

剩下的操作就都是周而复始的操作了,这里篇幅原因就不在演示了。可以看到打印出来的顺序就是先序遍历的顺序。

1.2、先序遍历非递归代码实现 

	public static void preOrderUnRecur(Node head) {
		if (head != null) {
			Stack<Node> stack = new Stack<Node>();   //首先准备一个栈stack
			stack.add(head);   //将头结点入栈
			while (!stack.isEmpty()) {  //当栈不为空时执行循环

                head = stack.pop();     //从栈中弹出结点
				System.out.print(head.value + " ");  //打印(或是处理)
				if (head.right != null) {     //右孩子不为空,则入栈
					stack.push(head.right);
				}
				if (head.left != null) {      //左孩子不为空,则入栈(必须遵循先右后左的入栈顺序)
					stack.push(head.left);
				}
			}
		}
	}

2、中序遍历

先说结论:首先准备一个栈,执行循环操作:

  1. 先将整棵树的左边界的所有结点依次入栈。
  2. 依次从栈中弹出结点记为cur,并打印(或其他操作)cur。依次判断cur是否有右子树,有则将右子树的整棵树左边界的所有结点依次入栈。
  3. 周而复始。

压栈顺序:(左全)右

2.1、详细图解描述 

 下面进行详细步骤描述:  

 首先将整颗数的左边界依次入栈,即1,2,4。

从栈用弹出结点4记为cur,打印cur并判断右子树是否存在,此时没有则继续从栈用弹出结点2记为cur并打印。

判断cur的右子树是否存在,此时右子树存在,则以结点5为整棵树的头结点,将整棵树的左边界的所有结点依次入栈,这里的左边界只有5结点一个,即将5结点入栈。

从栈用弹出结点5记为cur,打印cur,此时cur不存在右子树,则继续从栈中弹出结点1记为cur并打印。

此时cur(即结点1)存在右子树,则以结点3为整棵树的头结点,将整棵树的左边界的所有结点依次入栈,即3和6。

继续从栈中弹出结点6记为cur并打印,此时结点6没有右子树,则继续从栈中弹出结点3并打印。

最后结点3存在右子树,右子树的左边界只有7,将7入栈。然后从栈中弹出7并打印,7没有孩子节点,不操作。当再从栈中弹出结点时发现已空,则结束循环。 

2.2、中序遍历非递归代码实现  

	public static void inOrderUnRecur(Node head) {
		if (head != null) {
			Stack<Node> stack = new Stack<Node>();    //栈stack
			while (!stack.isEmpty() || head != null) { //栈不为空或者此时的头结点不为空
				if (head != null) {
                    //如果head不为空,则入栈,然后head指向下一个左孩子结点,继续判断是否为空,不为空继续执行
					stack.push(head);
					head = head.left;
				} else {
                    //当为空时从栈中弹出结点head,然后打印出head,并将head指向head的右孩子结点
					head = stack.pop();
					System.out.print(head.value + " ");
					head = head.right;
				}
			}
		}
	}

3、后序遍历

先说结论:首先准备两个栈(一个遍历栈,一个收集栈),将头结点压栈进遍历栈后,执行循环操作:

  1. 遍历栈中弹出一个结点记为cur。
  2. 将cur压栈进收集栈中。
  3. 如果cur有左孩子的话,则将cur的左孩子压栈进遍历栈。
  4. 如果cur有右孩子的话,则将cur的右孩子压栈进遍历栈。(即先左再右),注意与先序非递归遍历进行区分。
  5. 周而复始。

最后当遍历栈中再无结点时,将收集栈中的内容全部出栈,出栈顺序即为后序遍历顺序。

压栈顺序:头左右

3.1、详细图解描述 

 下面进行详细步骤描述: 

首先先把头结点入遍历栈,即1结点入遍历栈。

遍历栈中弹出一个结点记为cur,此时的cur即为1。然后将cur入收集栈。如果cur有孩子的话,则将cur的左节点和右节点按左右顺序入遍历栈。即此时 结点2结点3 入遍历栈。

周而复始,从遍历栈中弹出一个结点记为cur,此时的cur即为3。然后将cur入收集栈。如果cur有孩子的话,则将cur的左节点和右节点按左右顺序入遍历栈。即此时 结点6结点7 入遍历栈。

周而复始,从遍历栈中弹出结点7记为cur,然后将cur入收集栈。此时cur没有左右孩子,所以不操作。

接下来的结点6也和结点7一样,从遍历栈中弹出后直接入收集栈

周而复始,从遍历栈中弹出结点2记为cur,然后将cur入收集栈。将cur的孩子结点4,5分别入遍历栈。

最后遍历栈中的5和4都没有孩子结点,因此全部出栈并入收集栈。

此时将收集完的收集栈依次出栈并打印,得到的序列就是后序遍历

3.2、后序遍历非递归代码实现   

	public static void posOrderUnRecur(Node head) {
		if (head != null) {
			Stack<Node> s1 = new Stack<Node>();   //遍历栈
			Stack<Node> s2 = new Stack<Node>();   //收集栈
			s1.push(head);  //头结点入遍历栈

			while (!s1.isEmpty()) {     //遍历栈不为空时遍历
				head = s1.pop();   //从遍历栈中出栈
				s2.push(head);     //将出栈结点入收集栈
				if (head.left != null) {    //当左孩子存在时入遍历栈
					s1.push(head.left);
				}
				if (head.right != null) {   //当右孩子存在时入遍历栈(必须遵循先左后右的入栈顺序)
					s1.push(head.right);
				}
			}
			while (!s2.isEmpty()) {     //当遍历栈结束后,将收集栈中的所有结点挨个打印
				System.out.print(s2.pop().value + " ");
			}
		}
	}

 

如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!

如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!

如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!

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

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

相关文章

品牌被低价侵权了怎么处理

各品牌在销售过程中&#xff0c;会不断拓展销售渠道&#xff0c;主要分为线上和线下两个类型&#xff0c;线下渠道的低价侵权相较于线上会难发现一些&#xff0c;线上低价可以通过实时监测的方式&#xff0c;发现低价链接&#xff0c;再针对链接中的不同侵权情况进行处理。 力维…

【算法练习Day16】找树左下角的值路径总和 从中序与后序遍历序列构造二叉树

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 找树左下角的值路径总和从中…

PDF编辑和OCR文字识别工具ABBYY FineReader PDF

ABBYY FineReader PDF是一款专业的OCR文字识别和PDF编辑工具&#xff0c;可以帮助用户更好地处理和管理PDF文档。以下是ABBYY FineReader PDF的一些特点&#xff1a; 1. 文字识别精准&#xff1a;ABBYY FineReader PDF具有强大的OCR文字识别功能&#xff0c;可以将PDF中的文字…

大数据学习(2)Hadoop-分布式资源计算hive(1)

&&大数据学习&& &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 承认自己的无知&#xff0c;乃是开启智慧的大门 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一下博>主哦&#x…

浅谈风力发电场集中监控系统解决方案

作为清洁能源之一&#xff0c;风力发电场近几年装机容量快速增长。8月17日&#xff0c;国家能源局发布1-7月份全国电力工业统计数据。截至7月底&#xff0c;全国累计发电装机容量约27.4亿千瓦&#xff0c;同比增长11.5%。其中&#xff0c;太阳能发电装机容量约4.9亿千瓦&#x…

绝地求生大吃鸡攻略,让你成为顶级战士!

近年来&#xff0c;绝地求生越来越受到玩家们的喜爱&#xff0c;吃鸡成为了很多人的娱乐方式。作为一个资深吃鸡玩家&#xff0c;今天我要和大家分享一些提高战斗力的干货&#xff0c;以及一些方便吃鸡作图与查询的实用工具。 首先&#xff0c;提高战斗力是吃鸡游戏中最重要的一…

青菜学蒸馒头

作为一个会写代码的厨师&#xff0c;做好一笼松软可口的馒头那是必修的基本功&#xff0c;今天我就来试验一把&#xff0c;具体过程如下&#xff1a; 一、材料准备 1、200克面粉 2、2克干酵母粉 3、35度左右温开水一碗 4、白糖少许 二、制作步骤 1、面粉的选择 面粉的种…

html调用手机打电话、发短信网页源码/热门挪车自动拨打电话、发送短信html源码

源码介绍&#xff1a; 这个是自动拨打发送挪车短信电话源码&#xff0c;纯html临时停车挪车网站源码。利用html拨打电话、发送短信链接&#xff0c;js拨打电话。可以html调用手机打电话、发短信功能。使用H5移动HTML特殊链接实现打电话,发短信,发邮件的功能&#xff0c;非常方…

回归算法全解析!一文读懂机器学习中的回归模型

目录 一、引言回归问题的重要性文章目的和结构概览 二、回归基础什么是回归问题例子&#xff1a; 回归与分类的区别例子&#xff1a; 回归问题的应用场景例子&#xff1a; 三、常见回归算法3.1 线性回归数学原理代码实现输出例子&#xff1a; 3.2 多项式回归数学原理代码实现输…

2023年中国汽车后市场行业研究报告

第一章 行业概况 1.1 定义 汽车后市场行业在中国的快速崛起&#xff0c;反映了汽车产业链的完善和消费者需求的多样化。这个行业涵盖了汽车销售后&#xff0c;围绕汽车使用过程中涌现的各类服务和交易活动。它不仅为消费者提供了汽车使用过程中所需的全方位服务&#xff0c;也…

【C/C++笔试练习】常见进制转换、宏的定义和特点、sizeof与strlen、字符串函数、统计回文、连续最大和

文章目录 C/C笔试练习1.常见进制转换&#xff08;1&#xff09;进制前缀&#xff08;2&#xff09;进制转换 2.宏的定义和特点&#xff08;3&#xff09;宏的定义&#xff08;4&#xff09;有关宏的计算 3.sizeof与strlen&#xff08;5&#xff09;sizeof和strlen的差别 4.字符…

2023年中国复合门产量、销量、产业链及市场规模分析[图]

复合门是一种由木材和人造板材等材料组合而成的门&#xff0c;具有较高的强度和稳定性。它采用多层材料交叉堆叠、胶合而成&#xff0c;能够有效防止门扇变形、开裂和变色等问题&#xff0c;同时还具备一定的防火、防潮和防虫功能。 复合门产业链 资料来源&#xff1a;共研产业…

【开题报告】如何借助chatgpt完成毕业论文开题报告

步骤 1&#xff1a;确定论文主题和研究问题 首先&#xff0c;你需要确定你的论文主题和研究问题。这可以是与软件开发、算法、人工智能等相关的任何主题。确保主题具有一定的研究性和可行性。 步骤 2&#xff1a;收集相关文献和资料 在开始撰写开题报告之前&#xff0c;收集相…

软件培训测试高级工程师多测师肖sir__html之作业11

html之作业 案例1&#xff1a; 截图&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>表单</title></head><body><table style"background-color:red" bo…

全新第二代SCB后备保护器:保护电器的后备力量

在电气设备中&#xff0c;浪涌保护器&#xff08;SPD&#xff09;是一种重要的防雷装置&#xff0c;它可以在电源线路中并联接入&#xff0c;当发生过电压或雷电冲击时&#xff0c;将其导向地线&#xff0c;从而保护后端设备免受损坏。然而&#xff0c;SPD本身也会因为长期使用…

【算法设计与分析】— —实现最优载的贪心算法

&#x1f383;欢迎大家前去观看我的算法设计与分析专栏&#xff1a; 算法设计与分析_IT闫的博客-CSDN博客 希望对大家有所帮助&#xff01; &#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java…

Java 客户端调用 WebService 接口的一种方式

文章目录 1. SoapUI 测试 WebService 接口2. Java 访问 WebService 接口 1. SoapUI 测试 WebService 接口 通过SoapUI创建一个SOAP Project&#xff1b; 项目名称自定义&#xff0c;WSDL地址维护WebService接口地址。点击OK即可 项目创建完成后&#xff0c;展开WebService项&…

孙哥分布式VIP课程

杜绝一两门课程割韭菜&#xff0c;杜绝引流之后换老师&#xff0c;全行业唯一支持全套试听的良心课程。 你目前学习提高跳槽是否有如下痛点 1、网上开源课程“琳琅满目”&#xff0c;学完后还是掌握的不够扎实&#xff0c;理解的不够透彻&#xff0c;学无所成2、学了若干知识…

浏览器插件开发爬虫记录

常用爬虫有各种各样的反爬限制&#xff0c;而如果是小数据量并且该网站反爬手段非常厉害的前提下&#xff0c;可以考虑使用浏览器插件作为爬虫手段 基本代码来源于这位博主分享的插件代码&#xff0c; 主要在他的基础上加了 请求代理、管理面板、脚本注入拦截到的请求数据和管…

Windows服务器获取本地文件夹文件

1、直接复制粘贴 通过远程连接到这个服务器&#xff0c;然后本机到服务器能直接粘贴复制文件上去 注&#xff1a;首先服务器要先开启远程桌面哦 2、Windows远程连接 有的不能复制粘贴的&#xff0c;可以用第二种方法。 ①、windowsR,输入mstsc ②、点击“选项”按钮&#x…