广度优先遍历类似于二叉树的_二叉树的各种遍历方法的简单解释

05b68fa5bfdf1757aabfd06826f75770.png

二叉树顾名思义,最多两个孩子。

一般规定一个二叉树,因为节点间有相互连接的原因,所以只要给定根节点,那么顺着寻找左孩子和右孩子便可以遍历到所有的节点,这就是遍历的直观解释。

而遍历分为深度遍历和广度遍历(具体叫法我没有考证,大家也没有必要太深究一个名字本身,含义更重要是吧 ^.^ ),深度遍历类似于深度搜索的顺序,是深度优先遍历的循序。而广度遍历类似于广度搜索的顺序,是广度优先遍历的循序。我是从这方面区分的,为什么这么分类的原因在之后递归和遍历实现的时候就更能体现这个原因了。

具体看下图:

82a80a673c353504997cd9928f95b7c1.png
  • 前序遍历:根节点 -> 左子树 -> 右子树
  • 中序遍历:左子树 -> 根节点 -> 右子树
  • 后序遍历:左子树 -> 右子树 -> 根节点
  • 层序遍历:第一层,第二层,...

前中后序的叫法的不同是跟随根节点遍历的顺序改变的

遍历方法的递归和循环的python代码实现

1.1. 递归方式的前序遍历

使用递归的方式很容易理解:

先排除空节点,然后操作节点,然后左子树同样操作,右子树同样操作 简单解释一下,递归需要有终止条件,也就是空节点的时候。而且对于前序遍历的具体节点的遍历顺序需要明确知道——根节点,根节点的左孩子,然后是左孩子的左孩子,直到没有左孩子,然后是右孩子,然后是右孩子的左孩子...这么描述很难理解,所以之后有例子。

代码实现:

class TreeNode:def __init__(self, x):self.val = xself.left = Noneself.right = Nonedef preorderTraversal(root: TreeNode):if root is None:return# do something here, e.g. print itselfprint(root.val)preorderTraversal(root.left)preorderTraversal(root.right)

很简单,其中的类“TreeNode”的代码之后不再赘述。需要运行一下

if __name__ == '__main__':root=TreeNode(1)root.left=TreeNode(2)root.right=TreeNode(3)root.left.left=TreeNode(4)root.left.right=TreeNode(5)root.right.left=TreeNode(6)root.left.left.right=TreeNode(7)preorderTraversal(root)

这个就是这样一个二叉树:

1d230e206159c141aff37f0e626decb0.png

输出是这样的:

1
2
4
7
5
3
6

遍历顺序看图体会一下。

1.2.循环方式的前序遍历

这里插上一句,循环本身和递归是表达的同样一个算法核心,所以呢,不要过于恐惧循环的写法,而且不要太希望递归修改成循环会有很大的速度提升。

我们首先需要处理根节点,然后是根节点的左子树,那么之后处理根节点的右子树的话,我们就需要事先储存根节点,方便之后需要右子树,同理,每一个节点都需要如此操作。而且我们完全处理完一个左子树之后,我们紧接着需要这个左子树的父节点,那么就是最近存储的节点。综上,我们需要的操作是储存节点,而储存的容器是栈,因为后进先出。

为了统一化这种方法(递归转化为循环),规则化一些内容:将现在的节点计算出其他节点称为:

,对于现在的节点的执行称为:
,而在栈中的数据是该节点和是否被扩展过。

所以流程就是:

  1. 初始化时将根节点加入栈,标记未扩展
  2. 进行循环,直到栈空
  3. 循环中,取出节点。先判断节点非空,如果标记为未扩展,那么扩展左右孩子加入栈中,先加右孩子,然后是左孩子(因为我们希望的循序是根->左->右,而我们使用的是栈),都标记为未扩展,最后是本节点,标记为扩展;如果标记为扩展,那么进行操作

可能你会学过其他的前序循环的写法,会发现将第三步中这个节点标记是没有意义的,不使用标记一样可以实现(因为这个是一种尾递归的缘由,就是递归部分在操作之后),这没错,但是之后中序后序遍历的时候就要吃苦头了。

按照流程我们编写程序:

def preorderTraversal(root: TreeNode):stack=[(root,False)]while len(stack)>0:tree,extend=stack.pop()if tree is None:continueif not extend:stack.append((tree.right,False))stack.append((tree.left,False))stack.append((tree,True))else:# do something here, e.g. print itselfprint(tree.val)

当然可以舍弃扩展概念(笔者不推荐):

def preorderTraversal(root: TreeNode):stack=[root]while len(stack)>0:tree=stack.pop()if tree is None:continuestack.append(tree.right)stack.append(tree.left)# do something here, e.g. print itselfprint(tree.val)
````
## 2.1. 递归方式的中序遍历
```python
def inorderTraversal(root: TreeNode):if root is None:returninorderTraversal(root.left)# do something here, e.g. print itselfprint(root.val)inorderTraversal(root.right)

2.2.循环方式的中序遍历

和之前及其相似,中序后序上的理解和写法的也是很简单的

def inorderTraversal(root: TreeNode):stack=[(root,False)]while len(stack)>0:tree,extend=stack.pop()if tree is None:continueif not extend:stack.append((tree.right,False))stack.append((tree,True))stack.append((tree.left,False))else:# do something here, e.g. print itselfprint(tree.val)

3.1. 递归方式的后序遍历

def postorderTraversal(root: TreeNode):if root is None:returnpostorderTraversal(root.left)postorderTraversal(root.right)# do something here, e.g. print itselfprint(root.val)

3.2.循环方式的后序遍历

def postorderTraversal(root: TreeNode):stack=[(root,False)]while len(stack)>0:tree,extend=stack.pop()if tree is None:continueif not extend:stack.append((tree,True))stack.append((tree.right,False))stack.append((tree.left,False))else:# do something here, e.g. print itselfprint(tree.val)

3. 层序遍历

这个层序遍历有点特殊,之前的分类中它被分为广度遍历中,因为这个遍历是一层一层的,也就是说,如果使用循环表示的话,它的结构不像栈,而像队列,需要先进先出。

对于递归和循环的转换上,我主张的是递归一定可以转换成循环,而循环不一定能转换成递归,而递归有一种解决到底然后再解决其他问题的特点,也就是深度,需要使用对应循环写法中的栈,这些讨论之后我会出一篇文章仔细讨论,这里提一提,如果认为有兴趣的话,可以关注我一下,这一星期我就会写那一篇。 所以呢,我没有找到层序遍历递归的方法,就算存在这样的方法,也不是很必要,毕竟已经失去递归的简单直观的特点。

所以流程就是:

  1. 初始化时将根节点加入队列
  2. 进行循环,直到队列空
  3. 循环中,取出节点。先判断节点非空,扩展左右孩子加入栈中,先加左孩子,然后是右孩子,并且进行该节点的操作

这个流程相对简单,有点像前序遍历的简单写法

def levelOrderTraversal(root: TreeNode):stack=[root]while len(stack)>0:tree=stack.pop(0)if tree is None:continuestack.append(tree.left)stack.append(tree.right)# do something here, e.g. print itselfprint(tree.val)

结语

如果您喜欢,不妨鼓励支持一下,谢谢

推荐

  1. leetcode:二叉树的前序遍历
  2. leetcode:二叉树的中序遍历
  3. leetcode:二叉树的层序遍历
  4. 这篇文章对应的代码

注释

[1]:“现在的节点计算出其他节点称为:扩展[1]”:为什么叫做扩展,是因为节点到其他节点,其他节点的数量可能是多个,也可能是1个,也可能是0个,所以扩展合适一些

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

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

相关文章

基于jsf的项目_JSF基于事件的沟通:过时的方法

基于jsf的项目用JSF编写的Web应用程序由相互交互的bean组成。 在开发Web应用程序时,bean之间的通信是主要的设计模式之一。 有时,一个bean需要将事件发送给其他bean,以通知它们某些更改或其他任何更改。 通常,我们可以将托管bean或…

构建XML

构建XML举个小例子 //构建入参xmlDocument document DocumentHelper.createDocument();Element root document.addElement("wss");Element lajgEle root.addElement("wsss");lajgEle.addElement("c").addText(c);lajgEle.addElement("n&…

[CATARCS_2017] Week 1

SAE J1939 1234567891011121314151617181920212223242526272829303132 SoF SIGSRRIDE 1234567891011121314151617181920212223242526272829303132 转载于:https://www.cnblogs.com/cragoncan…

oracle数据库如何写翻页_oracle数据库如何写翻页

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云数据库专家保驾护航,为用户…

使用Java 8 Lambda简化嵌套循环

对于每个经常需要在Java 8&#xff08;或更高版本&#xff09;中使用多维数组的人来说&#xff0c;这只是一个快速技巧。 在这种情况下&#xff0c;您可能经常以类似于以下代码的结尾&#xff1a; float[][] values ... for (int i 0; i < values.length; i) {for (int …

onpropertychange替代方案

onpropertychange替代方案1.onpropertychange的介绍 onpropertychange事件就是property(属性)change(改变)的时候&#xff0c;触发事件。 这是IE专有的&#xff01; 如果想兼容其它浏览器&#xff0c;有个类似的事件&#xff0c;oninput&#xff01; 可能大家会想到另外一个事…

SQL SERVER的锁机制(二)——概述(锁的兼容性与可以锁定的资源)

二、完整的锁兼容性矩阵(见下图) 对上图的是代码说明&#xff1a;见下图。 三、下表列出了数据库引擎可以锁定的资源。 名称 资源 缩写 编码 呈现锁定时&#xff0c;描述该资源的方式 说明 数据行 RID RID 9 文件编号&#xff1a;分页编号&#xff1a;Slot编号 用于…

vue数组变化视图_vue对象数组数据变化,页面不渲染

data() { // data数据return {arr: [1,2,3],obj:{a: 1,b: 2}};},// 数据更新 数组视图不更新this.arr[0] OBKoro1;this.arr.length 1;console.log(arr);// [OBKoro1];// 数据更新 对象视图不更新this.obj.c OBKoro1;delete this.obj.a;console.log(obj); // {b:2,c:OBKoro1}…

jsp里面编写java代码注意加双引号

jsp里面编写java代码注意加双引号 if("<%type%>""open"){document.getElementById("e").focus();}

angularjs1访问子组件_Vue学习笔记之组件的应用

Vue组件的应用&#xff1a;1、基础使用&#xff1a;第一步创建组件&#xff0c;第二步注册组件&#xff0c;第三步使用组件。在注册组件是需要用到template的属性。全局组件和局部组件组件的嵌套(父子组件)&#xff1a;注意先后顺序&#xff0c;先声明&#xff0c;后面才能用2、…

Qt 程序打包发布总结

1. 概述 当我们用QT写好了一个软件&#xff0c;要把你的程序分享出去的时候&#xff0c;不可能把编译的目录拷贝给别人去运行。编译好的程序应该是一个主程序&#xff0c;加一些资源文件&#xff0c;再加一些动态链接库&#xff0c;高大上一些的还可以做一个安装文件。 QT开发…

什么是activemq_什么是ActiveMQ?

什么是activemq尽管Active MQ网站已经对ActiveMQ进行了详尽的介绍&#xff0c;但我想在其定义中添加更多上下文。 从ActiveMQ项目的网站上&#xff1a; “ ActiveMQ是JMS 1.1的开源实现&#xff0c;是J2EE 1.4规范的一部分。” 这是我的看法&#xff1a;ActiveMQ是一种开源消…

jdk1.8 stream() 把List <String>变成String

jdk1.8 stream() 把List 变成StringList<String> zts ss.getZts(); if (!CollectionUtils.isEmpty(zts)){String zt zts.stream().collect(Collectors.joining(",", "", "")); }就可以变成1,2 这种形式

boost跨平台 c++_跨平台C++整数类型 之一 固定宽度整数(boost和C++11)

原来一直使用ACE的跨平台整数类型&#xff0c;比如&#xff1a;ACE_UINT32, 但是自己使用C的风格是明显的现代风格&#xff0c;以范型为主&#xff0c;所以最近开始逐步替换ACE的代码&#xff0c;改用boost库。在boost库中&#xff0c;standard integer types用来支持跨平台的整…

FTP服务器原理

21.1 FTP服务器原理使用明码传输方式&#xff0c;且有相当多的安全危机历史。因此一般使用功能较少的vsftpd这个软件。21.1.1 FTP功能简介有以下功能文档传输与管理不同等级的用户身份&#xff1a;user&#xff0c;guest&#xff0c;anonymous命令记录与登录文件记录限制用户活…

jdk1.8 stream() 把List<Map<String,Object>> 变成Map<Object, List<Map<String, Object>>>

jdk1.8 stream() 把List<Map<String,Object>> 变成Map<Object, List<Map<String, Object>>>List<Map<String,Object>> list jdbc.queryForList(sql); Map<Object, List<Map<String, Object>>> map list.stream(…

java对两个表进行排序_Excel工作簿中多个worksheet工作表,如何对工作表进行排序?...

案例&#xff1a;如下图所示&#xff0c;一个Excel文件中&#xff0c;包含了多个工作表&#xff0c;有1-雷哥office&#xff0c;2-雷哥office......但是&#xff0c;我们发现工作表的顺序是比较混乱的&#xff0c;为了方便管理&#xff0c;如何对工作表进行排序呢&#xff1f;方…

mysql5.7用户管理

添加用户 命令&#xff1a;create user usernamehost identified by password 例子&#xff1a;create user changfeng% identified by 111111 配置用户权限 命令&#xff1a;grant privileges on database.table to usernamehost 例子&#xff1a;grant all on *.* to changfe…

jdk1.8 stream() 把List对象 变成String

jdk1.8 stream() 把List对象 变成StringString dm list.stream().map(XXX -> XXX.getDm()).collect(Collectors.joining(",", "", ""));有时候获取到一个List<XXX>(XXX是对象),因为业务需求,我们想要获取其中一个属性&#xff0c;变成…

Spring Cloud Ribbon –进行安全呼叫

很简单&#xff0c;但是最近我为此感到困惑 –我必须对安全的远程服务进行基于Netflix Ribbon的客户端调用。 事实证明&#xff0c;使用Netflix Ribbon可以通过两种方式实现这一点&#xff0c;我将通过Spring Cloud对Ribbon库的出色支持来演示这一点。 在之前的两个博客文章中…