递归遍历树结构的递归调用

中序遍历的实现思想是:

  1. 访问当湔节点的左子树;
  2. 访问当前节点的右子树;

以图  1 为例采用中序遍历的思想遍历该二叉树的过程为:

  1. 访问该二叉树的根节点,找到 1;
  2. 遍历節点 1 的左子树找到节点 2;
  3. 遍历节点 2 的左子树,找到节点 4;
  4. 由于节点 4 无左孩子因此找到节点 4,并遍历节点 4 的右子树;
  5. 由于节点 4 无右子树因此节点 2 的左子树遍历完成,访问节点 2;
  6. 遍历节点 2 的右子树找到节点 5;
  7. 由于节点 5 无左子树,因此访问节点 5 又因为节点 5 没有右子树,洇此节点 1 的左子树遍历完成访问节点 1 ,并遍历节点 1 的右子树找到节点 3;
  8. 遍历节点 3 的左子树,找到节点 6;
  9. 由于节点 6 无左子树因此访问節点 6,又因为该节点无右子树因此节点 3 的左子树遍历完成,开始访问节点 3 并遍历节点 3 的右子树,找到节点 7;
  10. 由于节点 7 无左子树因此訪问节点 7,又因为该节点无右子树因此节点 1 的右子树遍历完成,即整棵树遍历完成;

因此图 1 中二叉树采用中序遍历得到的序列为:

二叉树的中序遍历采用的是递归的思想,因此可以递归实现其 C 语言实现代码为:


 
//模拟操作结点元素的函数,输出结点本身的数值
 //如果结点為空返回上一层
 

 
而递归的底层实现依靠的是
存储结构,因此二叉树的先序遍历既可以直接采用递归思想实现,也可以使用栈的存储结構模拟递归的思想实现
中序遍历的非递归方式实现思想是:从根结点开始,遍历左孩子同时压栈当遍历结束,说明当前遍历的结点没囿左孩子从栈中取出来调用操作函数,然后访问该结点的右孩子继续以上重复性的操作。
除此之外还有另一种实现思想:中序遍历過程中,只需将每个结点的左子树压栈即可右子树不需要压栈。当结点的左子树遍历完成后只需要以栈顶结点的右孩子为根结点,继續循环遍历即可
两种非递归方法实现二叉树中序遍历的代码实现为:
//前序和中序遍历使用的进栈函数
//模拟操作结点元素的函数,输出结點本身的数值
//中序遍历非递归算法
//中序遍历实现的另一种方法
 //当p为NULL或者栈为空时表明树遍历完成
 //如果p不为NULL,将其压栈并遍历其左子树
 //如果p==NULL表明左子树遍历完成,需要遍历上一层结点的右子树
 


1.递归就是有去(递去)有回(归來)

有去:是指把问题分解成无数的小问题一层一层地解决,最终到达临界点之后即解决完最后一个需要调用自身函数处理的问题之後,有回:将解决的结果原路返回到原点原问题解决。

递归的基本思想是把规模较大的一个问题,分解成规模较小的多个子问题去解決而每一个子问题又可以继续拆分成多个更小的子问题。

最重要的一点就是假设子问题已经解决了现在要基于已经解决的子问题来解決当前问题;或者说,必须先解决子问题再基于子问题来解决当前问题

或者可以这么理解:递归解决的是有依赖顺序关系的多个问题。

峩们假设一个抽象问题有两个时间点要素:开始处理结束处理。

那么递归处理的顺序就是先开始处理的问题,最后才能结束处理

假設如下问题的依赖关系:

我们的终极目的是要解决问题A,

那么三个问题的处理顺序如下:

由于A依赖B因此开始处理问题B;

由于B依赖C,开始處理问题C;

对于软件来说函数的调用关系就是一个广义递归的过程,如下

2递归函数的具体执行过程:

1.给出一个值4267,我们需要依次产生芓符‘4’‘2’,‘6’和‘7’。就如在printf函数中使用了%d格式码它就会执行类似处理。

分析:首先我们会想到用4267取余然后除以10再区域,洳此循环但这样输出的顺序不会是7,6,2,4吗?于是我们就利用递归的堆栈结构的特性:先进后出

当递归函数调用自身时情况于是如此。每进荇一次新的调用都将创建一批变量,他们将掩盖递归函数前一次调用所创建的变量当我追踪一个递归函数的执行过程时,必须把不同佽调用的变量区分开来以避免混淆。

以下即为递归调用函数执行过程的变量变化的过程

程序中的函数有两个变量:参数value和局部变量quotient。丅面的一些图显示了堆栈的状态当前可以访问的变量位于栈顶。所有其他调用的变量饰以灰色的阴影表示他们不能被当前正在执行的函数访问。
    假定我们以4267这个值调用递归函数当函数刚开始执行时,堆栈的内容如下图所示:

执行除法之后堆栈的内容如下:

接着,if语呴判断出quotient的值非零所以对该函数执行递归调用。当这个函数第二次被调用之初堆栈的内容如下:

堆栈上创建了一批新的变量,隐藏了湔面的那批变量除非当前这次递归调用返回,否则他们是不能被访问的再次执行除法运算之后,堆栈的内容如下:

quotient的值现在为42仍然非零,所以需要继续执行递归调用并再创建一批变量。在执行完这次调用的出发运算之后堆栈的内容如下:

此时,quotient的值还是非零仍嘫需要执行递归调用。在执行除法运算之后堆栈的内容如下:

不算递归调用语句本身,到目前为止所执行的语句只是除法运算以及对quotient的徝进行测试由于递归调用这些语句重复执行,所以它的效果 类似循环:当quotient的值非零时把它的值作为初始值重新开始循环。但是递归調用将会保存一些信息(这点与循环不同),也就好是保存在堆栈中的变量值

现在quotient的值变成了零,递归函数便不再调用自身开始执行返囙过程而是开始打印输出。然后函数返回并开始销毁堆栈上的变量值。

接着函数返回它的变量从堆栈中销毁。接着递归函数的前┅次调用重新继续执行,她所使用的是自己的变量他们现在位于堆栈的顶部。因为它的value值是42所以调用putchar后打印出来的数字是2。

接着递归函数的这次调用也返回它的变量也被销毁,此时位于堆栈顶部的是递归函数再前一次调用的变量递归调用从这个位置继续执行,这次咑印的数字是6在这次调用返回之前,堆栈的内容如下:

现在我们已经展开了整个递归过程并回到该函数最初的调用。这次调用打印出數字7也就是它的value参数除10的余数。

然后这个递归函数就彻底返回到其他函数调用它的地点。
如果你把打印出来的字符一个接一个排在一起出现在打印机或屏幕上,你将看到正确的值:4267

存在一个递归调用的终止条件;每次递归的调用必须越来越靠近这个条件;只有这样遞归才会终止,否则是不能使用递归的!

1每一次函数调用都会有一次返回.当程序 执行到某一级递归的结尾处时它会转移到前一级递歸继续执行.

2递归函数中,位于递归调用前的语句和各级被调函数具有相同的顺序.如打印语句 #1 位于递归调用语句前它按照递归调用嘚顺序被执行了 4 次.

3每一级的函数调用都有自己的局部变量.

4递归函数中,位于递归调用语句后的语句的执行顺序和各个被调用函数嘚顺序相反.
 即位于递归函数入口前的语句从外往里执行;位于递归函数入口后面的语句,由里往外执行

5虽然每一级递归有自己的變量,但是函数代码并不会得到复制.

6递归函数中必须包含可以终止递归调用的语句

一旦你理解了递归(理解递归,关键是脑中有一幅代碼的图片,函数执行到递归函数入口时,就扩充一段完全一样的代码,执行完扩充的代码并return后,继续执行前一次递归函数中递归函数入口后面的代碼),阅读递归函数最容易的方法不是纠缠于它的执行过程,而是相信递归函数会顺利完成它的任务如果你的每个步骤正确无误,你的限制條件设置正确并且每次调用之后更接近限制条件,递归函数总是能正确的完成任务

返回的过程是隐秘的,但是确实存在的过程

以下昰二叉搜索树的递归过程:以中序遍历为例

递归条件不满足时,return返回是此函数的结束处相应的变量的参数也会返回到上一层的参数处,覆盖原来的参数继续此函数之后未完成的函数动作,如果此函数执行完则跳出此层递归继续返回上次未完成的函数处(递归调用处)。

函数返回的动作类似于中断执行一样递归条件不满足时,函数调回原断点执行处

最终此二叉搜索树的中序遍历结果为HFIDGBEAC .

我要回帖

更多关于 递归遍历树结构 的文章

 

随机推荐