深度优先遍历与树的前中后序遍历

本文探讨了深度优先遍历与树的前中后序遍历之间的联系和区别,指出这些遍历方式都是深度优先遍历在树结构上的特例。同时,文章还详细比较了深度优先遍历的两种非递归实现方式:完全展开和非完全展开,分析了它们在不同情况下的效率和空间需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文主要讨论深度优先遍历与树的前中后序遍历的联系与区别,并对深度优先遍历的几种非递归实现方式进行了对比。

深度优先遍历与树的前中后序遍历的联系和区别

树的前中后序遍历是深度优先遍历应用在树结构上的一(几)种特例。我们都知道树的前序遍历的遍历顺序为:当前节点,左子树,右子树。中序遍历的遍历顺序为:左子树,当前节点,右子树。后序遍历的遍历顺序为:左子树,右子树,当前节点。(这里我使用当前节点表示当前正在处理的节点,为了跟下面深度优先遍历对应)。其实同时还有这么几种遍历方式(1)当前节点,右子树,左子树(2)右子树,当前节点,左子树(3)右子树,左子树,当前节点。这种比较不常规的遍历方式在解决某些题目的时候比较实用。


深度优先遍历通常情况下是访问当前节点,然后访问当前节点的第一个邻居(图),第二个邻居(图)……最后一个邻居(图)。之所以说是通常情况下,是因为还有非通常情况下,我们也可以先访问第一个邻居(图),然后访问当前节点,然后在访问剩下的邻居(图)。事实上,还有另外一种划分法,比如我们将当前节点的邻居根据某种属性(比如树中的左、右)或者方式化成n种邻居,那么先访问第一种邻居中的第一个,第二个,……,第二种邻居中的第一个,第二个……,访问当前节点可以插入其中任何地方。恩,就是这个意思,他们都是深度优先遍历。这里的区别主要在与邻居与当前节点的访问顺序。


这样对比起来,可以很明显的看出来,树的前中后序遍历就是深度优先遍历在树上的特例。前序遍历就对应于将邻居划分两类,先访问当前节点然后在访问邻居节点的情况。而后序遍历对应于将邻居划分为两类,先访问邻居节点然后访问当前节点的情况。中序遍历呢,就是先访问第一类邻居,然后访问当前节点,然后再访问第二类邻居。

深度优先遍历的几种非递归实现方式

我们通常实现的深度优先遍历,都是对应着树的前序遍历的一种,也就是说访问当前节点,然后再去访问它的相邻节点。我们这里讨论的是非递归实现,那么当然就会有一个用于替代递归的栈。

为了方便描述,我们将当前节点的邻居节点的入栈(进入待访问集合)的情况称作该节点的展开程度,即如果邻居节点全部入栈的话,那么叫做该节点完全展开了,否则叫做该节点非完全展开。我们从栈中存储的节点的情况来进行分类,主要有以下几种:
  1. 栈中存储的节点全部都是未访问过的。这里仍旧可以按照节点的展开程序分为两类。先说常见的第一类,完全展开。在这种情况中,访问当前节点后就立马将其出栈,然后入栈其所有的邻居节点。第二类,就是非完全展开了。在这种情况中,得有相应的数据结构支持,也就是说从当前节点,可以知道访问当前节点的父节点的第二个儿子(就是邻接表的那种结构,一个节点的所有邻居是一个链表)。访问完当前(栈顶)节点,将其出栈,然后根据当前节点找到其父节点的第二个儿子,将其入栈,然后再入栈当前节点的第一个未访问的邻居。不过感觉上,这种需要的数据结构有点奇葩。举个简单的列子,在树结构中,除了要父子节点的连接外,还要左儿子和右儿子相连接(这里不是指实际拓扑上相连,而是从左儿子可以访问到右二子)。

    非完全展开的,栈中存储的节点比较少,在节点的度比较大的时候比较有效,相反完全展开的,在度比较大的情况下,就需要很大的存储空间,尤其搜索空间再更大的时候,就比较占空间了。这个数量级大概是O(深度*度)。第一类完全展开的,使用起来比较无脑,只用push,pop就行了,不用考虑其他的。而第二类,对数据结构要求有点奇葩。这种情况,由于栈中并未存储已经访问的节点,要获得到当前的路径,并不是很方便,需要自己另开空间来记录。
  2. 栈中存储的节点,刚好是从开始节点到当前节点的一个路径。当然,它一定包含了某些已经访问的节点,并且显然节点并没有完全展开,并且只有栈顶元素是未访问过的。分析同上,这个也需要存储当前的展开位置,也就是说它的邻居节点展开到哪了,下一次该谁了亦或是没有下一个了。这种情况下,访问当前(栈顶)节点,入栈其第一个邻居,如果没有邻居或者邻居已经访问过了,那么就出栈一直到一个没有完全展开的节点,然后继续展开。好处是显而易见的,占用空间只与深度有关,并且很容易得到当前路径(比如使用vector来实现栈)。
  3. 栈中存储的节点,不仅仅包含了到当前节点的一个路径。当然每个节点是完全展开的,并且也包含了已访问过的节点。这种实现方式,通常是多余的,不过在某些访问顺序下需要这样做。在先访问,在入栈的话,貌似没有什么好处尴尬。除非要先访问邻居节点,然后再访问自己,这种就类似于后序遍历了。
那么鼎鼎大名的回溯法是属于哪种情况呢?据 这里

深度优先搜索法与前面讲的回溯法差不多,主要的区别是回溯法在求解过程中不保留完整的树结构,而深度优先搜索则记下完整的搜索树,搜索树起记录解路径和状态判重的作用。为了减少存储空间,在深度优先搜索中,用标志的方法记录访问过的状态,这种处理方法使得深度优先搜索法与回溯法没什么区别了。在回溯法中,我们己分析了非递归的实现过程,在这里就只讨论深度优先的递归实现方法。

好吧,他们的区别不在于栈中存储的节点。但是这个没有关系,上面也说了,为了减少存储空间,他们两个已经变的差不多了。可以认为一样吧,具体实现上就可以参考上面的三种方式了。

以上是我自己的一点拙见,欢迎讨论。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值