2025考研计算机408数据结构+操作系统+计算机组成原理+计算机网络

2025考研计算机408数据结构+操作系统+计算机组成原理+计算机网络

链–接:https://pan.baidu.com/s/1yJGi5KZq-s8jT8ffa670Ew?pwd=eqqh
提-取-码:eqqh

二叉树的先序,中序和后序遍历算法。好,首先来看一下什么是便利,其实便利这个词我们在之前的讲解当中已经用过很多次了,就是指按照某种规定的次序,把所有的节点都访问一遍。像之前我们学习的线性结构,对这种数据结构的便利规则是很简单的,比如我们可以从头开始。往后面依次遍历或者从尾开始,往前面依次遍历。这是线性结构,

但是对于树形结构来说,便利规则的制定就会更复杂一些。那我们之前说过,树这种逻辑结构呈现出了一层一层的分层特性,所以不难想到,其实我们可以利用它的这种分层特性。制定一种便利规则,一层一层的来访问这些节点,那这种便利方式就叫层次便利。层次便利的实现,我们会在下小节中进行讲解,那这个小节中我们要学习的三种便利算法,它是根据二叉树的递归特性来制定的便利规则。对于任何一棵二叉树,它要么是一个空二叉树,

要么就是由根结点和左右子树组成的二叉树。当然,左右子树也有可能是空二叉树。好,那基于这种递归特性,我们可以制定这样的规则,如果我们当前要遍历的二叉树,它是一个空二叉树的话,那我们什么都不用做。而如果此时遍历的是非空二叉树,那我们可以根据这三个部分被访问的顺序,制定这样的三种规则,分别是先序,中序和后序遍历。所谓先序遍历,

就是指先访问,根结点。再访问左子树,最后是访问右子树。那如果左子树它还有下一级的结点,那我们会同样按照先续便利的规则来便利这个左子树。也就是先访问根结点,再访问下一级的左子树,最后是右子树,那这是先续遍历。当然,它还有另外一个名字叫先更便利,因为我们是先访问根节点。那中序遍历,就是指先中序遍历左子树,

再访问根结点。最后中序遍历右子树,左跟右。类似的后续遍历就是先后续遍历左子树,再后续遍历右子树,最后来访问根结点左右根。当然,中序遍历也可以叫中根遍历,后序遍历也可以叫后根遍历。好,下面看几个例子,对这样的一棵简单的二叉树执行先序遍历,那么先序遍历的顺序应该是跟左右。所以各个节点被访问的顺序应该是根左右,也就是ABC。

那如果用中序遍历的规则访问各个节点的顺序,就应该是左跟右在中间访问根结点,因此中序遍历得到的遍历序列就应该是。bac好最后采用后续编译的规则,应该是左右根,因此各个节点被访问的顺序应该是BC a。好,这是一棵最简单的二叉树。下一个例子,我们把它的右孩子干掉,也就是右子树是一个空树,先看先续遍历,按照根。左右的顺序来访问这些节点,那右子说的节点为空,

所以跟左右得到的访问次序就应该是AB。接下来,中序遍历左跟右得到的序列应该是BA。后续病例左右根得到的依然是BA。类似的,如果左子树为空,那么得到的先中后序遍历序列应该是这个样子。好,再看一个更复杂的例子,对这棵树进行先序遍历,那么按照根左右的访问顺序,我们可以知道肯定是先访问完a。接下来才访问b好,但是由于b它是一个分支结点,而不是叶子结点,

所以我们应该递归的对b这棵子树进行先续遍利。那先续并列的顺序应该是跟左右,所以在访问完b这个结点之后,应该紧接着访问d和e这两个结点,因此我们把d和e写在b的后面。好类似的,对于c这棵子树来说,同样的是递归的,采用先序遍历根左右,因此对这棵子树的访问顺序应该是cfg。那这是采用先序遍历的规则,对各个节点的访问顺序,接下来中序遍历也就是左跟右这样的顺序。所以左跟右肯定是先访问完b之后才会访问,根结点a最后才会访问右边的。

右孩子节点c。好同样的b,它是一个分支结点,所以按中序遍历访问这棵子树,应该是左跟右,也就是d be这样的顺序,所以在访问b之前肯定是需要先访问d。访问b之后再访问e。类似的对于右边这棵子树,按照中序遍历左跟右的规则,应该是fcg。那这就是中序遍历的遍历序列。好,最后是后续遍历,那按照左右根的顺序,

肯定是先访问b之后才可能是c,最后是a。好,现在b这棵子树同样需要递归的,对它进行后续遍历,也就是左右根,因此访问b之前应该要先访问d和e。而对于c这棵子树,在访问c之前应该是左右根,所以应该是fgc。好,那这是后续遍历所得到的遍历序列。其实这些叶子节点,我们应该把它理解为它下边还连了两个空子数,所以对这样的一棵子树进行先序遍历,

跟左右得到的序列是d。中序遍历左跟右,得到的依然只有一个d,那后续遍历也类似,只有一个d。那给定义课二叉树确定它的三种便利序列,这是我们考研当中经常考的内容,所以我们再进行一个练习。对这棵树执行先序遍历,也就是根左右那么肯定是先访问a接下来。a之后才有可能访问b,最后是c好,现在由于b它不是叶子,它是一个分支节点,所以我们依然需要用跟左右的方式把它展开,

那跟左右就应该是b。de.同样的c也是一个分支,所以把这棵子树展开就应该是根左右就应该是CF。好,接下来还剩d这个结点,它是一个分支结点,那根左右得到的序列就应该是dg,所以在访问完d之后还应该紧接着访问g。那这样我们就得到了先续并列序列abd GE CF。好下面中序遍历左跟右,所以应该是bac那b和c都是分支结点,所以需要递归的,把它们展开成中序遍历序列。对于b这棵子树来说,

左跟右得到的应该是d be对c这棵子树,左跟右得到的应该是FC。好,最后还有一个分支结点d,我们没把它展开,那左跟右得到的应该是dg。那这样就得到了中序遍历序列好,最后是后续遍历,也就是左右根,那左右根因此肯定是先访问b,接下来c最后才是根a。再把b展开左右根,因此得到deb好c也要展开。左右根所以得到的应该是FC,最后再把d展开,

左右根所以得到的应该是gd。因此,得到后续病例序列g deb fca。那大家可以在稿纸上随便画几个二叉树,进行类似的练习好,那我们把这种手算确定便利序列的方法,称为分支节点逐层展开法。如果一个结点是分支结点,而不是叶子结点的话,那你就需要嵌套递归的,按照特定的便利序列把它展开到下一级。那这儿再给大家一个二叉树,大家可以试一试,把这棵二叉树进行先序,中序和后序遍历,

可以得到什么样的序列?那这棵二叉树是根据这样的一个算术表达式。得到的呃语法分析数。这个式子当中,我们需要先算c-d那c是左操作数d是右操作数,而减号是操作符,所以这个部分表示的是c-d。好c-d的结果还需要和b进行相乘,所以乘号左孩子是b,然后右孩子是刚才得到的这个结果。好,后续的就不再展开,应该很好理解,那大家可以暂停自己试一下,得到的先中后序遍历序列到底是什么?

这儿我们先直接给出答案好,那大家会发现对这一棵算术表达式分析树进行先序,中序,后序遍历所得到的这个遍历序列。其实对应的就是这个算术式,它的前缀表达式,中缀表达式和后缀表达式。这是我们在栈的应用,那个部分讲过的内容,当然这儿得到的中缀表达式是没有加括号,没有加界限符的,所以这个中缀表达式和原本的这个算术式其实是有差异的。因此,输出中缀表达式的过程中,我们需要在合适的地方加上界限符,

也就是小括号。那这个题目在我们之前有一年的真题当中考察过,大家会在课后习题中遇到这儿,我们只是想强调对算术表达式的分析数进行先中后序遍历。可以得到这个算术表达式的前缀,中缀和后缀表达式。好了,那到此为止,我们讲的都是怎么用手算的方式确定便利的顺序,接下来看一下代码怎么写?先看先续遍历很简单,我们写一个递归函数就可以实现,如果二叉树为空的话,也就是这个if条件不满足的话,那什么都不做。

那如果二叉树飞空,我们按先序遍历的规则,也就是根左右的顺序,应该先visit访问这个根结点。你可以在visit函数里边定义一些呃合适的操作,比如说打印这个结点的值等等。好根结点被访问完了之后,接下来是要先续遍历左子树,所以在这个地方我们需要递归的调用先续遍历这个函数。传入左子树的根结点,也就是当前这个根的左孩子。好遍历完左子树之后,最后是来先续遍历右子树。所以传入右孩子节点。这是先序遍历,

那中序和后序遍历的代码也是极其类似的。对于中序遍历,我们只需要把visit函数放在中间就可以。你看这儿是先递归的中序,遍历左子树,然后再访问根结点,最后是中序遍历右子树左跟右。类似的后续遍历的代码,我们只需要把visit放在最后面就可以。对于本专业的同学,这个递归算法应该是很简单的,但是对于跨考的同学来说,可能递归的过程会让人觉得有点绕。所以接下来想啰嗦几句,照顾一下跨考的同学,

我们以先续遍历的代码为例来看一下这个函数是怎么执行的。我们要先去遍历这棵二叉树,那我们传入的这个根结点就应该是a。不知道大家还记不记得,当我们在调用一个函数的时候,系其实系统会在背后给我们开辟一个函数调用站。在这个栈里边,会用来保存当前执行的函数的一些啊,必要的信息。那当前我们调用的是preorder这个函数。并且我们传入的这个参数t,它其实指向的是a这个节点好,由于当前访问的节点非空,所以接下来会visit。访问这个节点。

因此a是第一个被访问的节点。好,接下来应该递归的遍历,它的左子树,也就是说这个函数会调用它自身,只不过传入的参数是当前节点的左孩子l child。那在进入下一层的函数之前,系统会在背后帮我们记录当前我们执行到了第126行代码。然后接下来会进入下一层的函数,只不过对于下一层的函数,它的这儿所谓的t其实指的是b这个结点。好,那对于这一层的调用,当前的t非空,所以我们会visit这个节点,

那此时t指向的其实是b这个节点,所以b是第二个被访问的节点。那接下来又要访问b的左孩子,所以同样的系统会记录当前这一层的函数,它执行到了第126行。然后把下一层函数的信息压到站顶,那这一层的t指向的是d这个结点好,接下来是访问d。再往后访问d的左孩子,而d的左孩子是一个空结点,所以接下来这一层的调用当中,我们传入的参数t。它等于n由于不满足这个条件,因此在这一层的函数当中,什么也不做,

就可以直接return返回。那这层函数执行结束之后,需要把它的信息弹出栈。这样的话就可以回到上一层的函数,也就是对d节点的处理,那通过这个信息我们知道之前执行的是126行,因此接下来是不是应该执行127行代码?好,所以接下来又会递归的,调用这个函数,只不过传入的参数变成了它的右孩子。也就是g这个结点。那在这层函数当中,应该先visit g这个结点,接下来访问g的左子树,

但是由于左子树为空,所以什么也不会做。再次回到g结点这一层函数。那刚才执行了126行,接下来应该执行127行,所以要访问它的右孩子,同样的,它的右孩子也是空,因此这一层的函数什么也不会做,所以会再次返回处理g结点的这层函数。好,刚才执行了127行代码,那后面是不是已经没有代码了,所以这一层函数的执行结束?同样的,

我们就可以返回上一层的函数,也就是处理d结点的函数,那同样的这层函数也已经执行了最后一句代码。所以这层函数也可以执行结束,这样就返回了处理b结点的啊这层函数。好,通过站里的信息,我们知道之前已经执行了126行代码,所以接下来应该执行127行。也就是要访问b的右子树好,那后面的过程都是类似的,相信大家已经能够理解,这就不再赘述。好,那可以看到这种递归实线的便利算法,

它的空间复杂度应该是o的h+1。这样的一个量级h指的是二叉树的高度。加一是因为最后这一层的叶子结点,它下面还有两个空结点,所以处理空结点的这一层函数依然需要把它的信息压到站顶。因此是o的h+1,当然我们可以把常数项舍去,所以这种递归算法,它的空间复杂度应该是o的h这样的一个量级。不知道大家有没有注意到,我们在画这些箭头的时候,红色箭头表示的是我们的函数执行流。第一次路过这个节点,而绿色箭头表示的是函数执行流。第二次回到这个节点。

然后紫色箭头表示的是我们的函数执行第三次回到这个节点。这些节点的处理路径有这样的一个规律,我们从根节点出发。先访问根结点,接下来如果当前结点的左边还有没走过的路,那么我们会优先往左走。所以接下来处理的是b好,从这个节点出发,它的左边还有没走过的路,所以会优先往左边走。因此,接下来处理的是d那d的左边是一个空树,所以当我们往左边走,走到路的尽头,遇到这个空结点空树的时候,

我们会让这个函数的执行流程往回走。也就是再次的回到d,这是第二次回到处理d的那个函数好,接下来对于d这个节点来说,它的左边已经已经无路可走了,所以接下来会选择往右边的路走。因此,接下来处理的是g这个结点,那g的左边是一个空结点,所以会返回右边也是一个空结点。也会返回那处理完右边这个空结点之后,其实是第三次返回了处理g的那一层函数。现在对于g结点来说,它的左边和右边都无路可走了,所以会返回上一层的调用,

也就是回到处理d结点的函数。好,现在对于d来说,左右的路都已经走过,因此同样的我们要让它往回走,也就是回到处理b的这层函数。好对于b来说,左边的路已经走完了,接下来应该往右边走,当第一次到达处理e的这层函数的时候,就会访问这个节点,所以e是接下来被访问的下一个节点。好,后面的过程就不再赘述,总之从这样的一个视角来看,

当我们画出这样的一条呃路径之后,每一个节点第一次被。路过的时候就会被处理访问。所以用这样的方式,我们也可以快速的确定出先序遍历,所得到的遍历序列。不难发现,每一个节点都会被录过三次,那类似的方法也可以用于分析中序和后续遍历。画路径的方法也是一样的,我们要先脑补出这个空节点,然后从根出发,如果左边还有路没走,那优先往左走,如果遇到空节点,

也就是走到路的尽头,我们就需要往回走。对于中序遍历来说,当第二次经过这个节点的时候,才会访问这个节点,所以第一个被访问的应该是d节点。好,后面的过程是类似的,当第二次路过一个节点的时候,才会对这个节点进行访问。这是中序遍历。最后看后续病例,只有我们最后一次,第三次经过一个节点的时候才会访问这个节点。所以用同样的方法可以确定后续遍历的顺序。

好,这个结点此时是第三次被录过,所以g是第一个被访问的结点,接下来是d。所以我们也可以从递归函数的执行原理呃,总结出一种方法来确定遍历序列。那我们给这个方法取一个名字,叫做从你的全世界路过。那同样的,大家可以用这棵二叉树进行一个练手,我们先补上这些空结点。好,接下来从根结点出发,我们来确定先序遍历序列。第一次路过结点的时候就要访问这个结点。

并且我们会优先往左走,只有左边的路走完之后才会往右边走,所以按照先序遍历的规则,访问的顺序应该是减号。加号a。乘号b。减号c。d.除号e。f好用这样的方式,我们可以很快速的得到一个先序遍历序列,那中序和后序大家也可以再尝试着练习一下。其实确定二叉树的遍历序列,这个只大家只需要用熟一种方法就可以看一下自己喜欢什么。好,

那这一小节中我们学习了二叉树的递归遍历的三种算法,如果考察这一章的算法题,那么大多是从这三种算法进行一个变化得来的。比如说我们来看这样的一个问题,要求一棵树的深度,那二叉树有这样的递归特性,分为根结点左子树。还有柚子树,所以我们要求一棵树的深度或者说高度的话,我们应该先递归的求出左子树的高度,然后是右子树的高度。接下来,我们选取左子树和右子树当中高度更高的一边,然后再加一。这样的话,

我们不就得到了树的深度吗?好,所以你看这个算法,这儿是访问左,这儿是访问右,这儿相当于是访问根,对吧?左右根其实它就是一个后续遍历算法的变种。因此,这一小节学习的三种便利算法是很重要的,大家在课后习题当中也会有更多的体会好,那这个小节中我们对先续便利算法的递归过程,它在背后的一个执行细节。花了比较多的时间分析,这是为了照顾到跨考的同学,

让大家理解函数执行背后的一个本质。大家需要熟练的掌握呃,一种求便利序列的方法,当然这两种方法的名字是呃,我胡乱编的,大家也可以用自己喜欢的方式来描述它。那对于求二叉树的变率序列,这种类型的题目大家一定要多加练习,需要做的又快又准。好的,那以上就是这一小节的全部内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值