递归函数的设计--以二叉树查找为例

工作后发现,大学计算机课程里面的树结构相当有用,很多现实中的东西把它看成树就容易理解多了。

----大学舍友

最近和二叉树有关的代码频繁地打交道。由于函数的递归实现占用巨大的计算机运行时间(尤其是深层递归调用,存储函数参数的程序栈会占据很大的运行时间,参见图1),我的目的是将递归实现改为非递归的循环实现(以前的编程经验表明,两者的运行速度相差悬殊)。本文以二叉树查找为例,对递归函数的设计进行分析说明,最后给出查找的非递归实现思路。

图1. 递归调用示意图

递归,就是函数调用自身的一种方式[1]。定义就是上面这个样子的,实际函数的调用过程如图 1 所示[2]。在程序的具体实现过程中,还是有两个问题需要格外注意:一,函数调用自身,那么调用前和调用后的区别在哪里?点解,两者函数的参数不一样,进而导致程序处理的问题域不一样。二,递归调用什么时候结束?点解,要在程序中针对每一种模式(下文将介绍)进行设计,避免程序陷入到死循环中。

具体到二叉树的查找实现上来。由于二叉树的查找基于遍历的方式来实现,教科书上关于遍历的方式大抵分为前序,中序和后序这三种方式进行讲解,并给出了伪代码。这里仅以前序方式为例进行说明(选择前序方式是基于两方面的原因:一,这种实现方式便于对递归函数的设计和理解;二,根据我的了解,后两种方式在编程实践中的使用貌似不多,只是提供理论上的一种实现方式)。基于前序的二叉树查找的伪代码如下:

Search() {

if(当前节点非空) {

1. 遍历当前节点。相应的退出条件为:验证其是否符合查找要求,如果符合,则退出

2. 遍历其左子树,调用Search()函数

3. 遍历其右子树,调用Search()函数

}

}

如前所述,遍历其左右子树分别需要将左子树和右子树作为Search()的函数参数传递进去,程序从根节点开始一层一层向下查找。另外,构造出来的二叉树有的时候常常不是完全二叉树,因此需要判断当前节点的左右子树是否为空,如果其中一个为空,那么就直接在另外一个子树中进行查找,如果两个都为空而当前节点又不符合查找要求,则退出。如下图所示,它给出了当前节点的五种模式[3],参见图2,它们分别是空树,当前节点没有左右子树,当前节点有左子树没有右子树,当前节点有右子树没有左子树,当前节点既有左子树又有右子树,在程序设计中要对这些方式都要考虑到。

图2. 二叉树节点的五种模式

其实,在设计基于二叉树算法的时候,只要将一个节点的五种模式考虑清楚基本就可以大功告成了。原因是递归调用是不断地向下一层调用的方式,即考虑某个节点后,紧接着考虑它的左右子树,而左右子树又属于五种情形之一,当然可以用相同的函数来执行了。

回到开头的问题,如何将递归调用改为非递归实现?可以根据二叉树的层次来用for循环表示,或者直接使用while循环判断节点是否为空来实现,这样就可以避免递归调用了。不过,相应的函数需要进行修改,伪代码如下(以for循环为例):

for(int i=1; i<total_level+1;++i) {

if(treeNode == NULL) {

return;

}

if(i == 1) {

if(data not in treeNode)

return;

}

successful = Search(data, treeNode);

if(successful ) {

break;

}

if (left_child !=NULL && data in left_child) {

 treeNode = treeNode->left_child;

continue;

}

if (right_child !=NULL && data in right_child) {

 treeNode = treeNode->right_child;

                   continue;

}

}

需要注意的是,上述代码建立在这样的假设之上,即在搜索的过程中,当判断出 data 在某个节点所在的子树下时,要保证 data 在这个子树下能够被 Search 到。否则,程序很可能不能正确地 Search 到相应的节点。

参考资料:

[1] http://baike.baidu.com/link?url=iJRpWUn-ez1s08MrNWyGoUPhVs21ijt0ynHRHUkEFN1hc67kXm5RkmA9SExU7i6TmtjbWaqhHt5DOEp8vTcD5gs5IJ9rnkKyeMwU8Pm4a1m

[2] https://www.zhihu.com/question/20507130

[3] http://www.tuicool.com/articles/ERni22

作者:caicailiu 出处:http://www.cnblogs.com/liuyc/  欢迎转载或分享,但请务必声明文章出处。

时间: 2024-10-13 20:05:58

递归函数的设计--以二叉树查找为例的相关文章

二叉树——查找两个任意节点的最近祖先

很久没有用过二叉树了,最近由于需要用到了,发现很多知识需要巩固了,中间涉及到一个算法就是找任意两个节点的最近祖先.通过本人回顾和演算,最终提出了下面一个方法,网上也有很多其他的方式实现,再次仅对自己好几个小时的工作作个记录和积累吧! 程序是用C语言写的,个人觉得如果用C#实现会更加方便. 首先是数据结构定义: [cpp] view plaincopyprint? typedef char TElemType; typedef bool Status; typedef struct BiTNode

Python中的二叉树查找算法模块

问题 思路说明 二叉树查找算法,在开发实践中,会经常用到.按照惯例,对于这么一个常用的东西,Python一定会提供轮子的.是的,python就是这样,一定会让开发者省心,降低开发者的工作压力. python中的二叉树模块内容: BinaryTree:非平衡二叉树 AVLTree:平衡的AVL树 RBTree:平衡的红黑树 以上是用python写的,相面的模块是用c写的,并且可以做为Cython的包. FastBinaryTree FastAVLTree FastRBTree 特别需要说明的是:树

数据结构之二叉树查找

数据结构之--二叉树查找 定义:它是一棵树,或者具有以下性质的树.  若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值: 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值:  它的左.右子树也分别为二叉排序树: 图解: ?        ?    ?    ?    ?    ?? #include<stdio.h> #include<stdlib.h> typedef int Status; #define TRUE 1 #define FALSE 0 t

查找二,二叉树查找与2-3树红黑树

BST: 每个节点的键,都大于其左自述中的任意节点的键,而小于有字数的任意结点的键. 部分实现 get(Node x , Key key){ if(x == null) return null; cmp = key.compareTo(x.key); if(cmp<0) retrun get(x.right,key); else if(cmp>0) retrun get(x.left,key); else return x.val; } 2-3树红黑树: 属于平衡查找树,为了希望保持二分查找树

第4章第1节练习题5 二叉树查找第k个结点的值

问题描述 求先序遍历序列中第k(0≤k≤二叉树中结点个数)个结点的值. 算法思想 求解先序遍历中第k个结点的值,设置一个全局变量cnt来记录已经访问过的结点序号,当k==cnt时,表示找到了满足条件的结点,打印输出.否则继续按照先序遍历的方式遍历整棵二叉树便可.实现过程参见算法描述1 经过上述的思想,并没有将我们所需要的结果返回回来,因此在上述算法的思想上再次进行修改.在递归函数中为了表示区分,当T==NULL时,返回#:当找到时,返回T->data.当cnt≠k时,则递归的在左子树中查找,若找

二叉树——查找两个随机节点最近的祖先

非常实用的太久没有一个二叉树,因为需要使用最近,我们觉得非常有必要巩固知识.中间涉及到一个随机算法是寻找两个节点的直接祖先. 我记得和牙石通过,于提出了以下一个方法,网上也有非常多其它的方式实现,再次仅对自己好几个小时的工作作个记录和积累吧! 程序是用C语言写的,个人认为假设用C#实现会更加方便. 首先是数据结构定义: typedef char TElemType; typedef bool Status; typedef struct BiTNode{ TElemType data; stru

二叉树查找

二叉树中查找算法: JAVA代码: /** * @author hbliu * @param data * @return 找到的结点 */ public Node searchNode(int data){ return searchNode(this.root,data); } /** * @author hbliu * @param node * @param data * @return * 功 能:二叉树中查找数据元素 */ private Node searchNode(Node n

二叉树查找之python实现--(插入)

借维基百科的话来说就是二叉树就是一种每个节点最多有两个子树的树结构.但是今天讨论的是二叉查找树,这个查找树就是二叉树的一种延伸吧,加了几条限制就变成了二叉查找树. 下面我们来看看二叉查找树有什么性质呢,如果左子树不为空,那么一定全部小于等于根节点,同样右子树也是一样的,而且左右子树都是二叉查找树.最后树中没有键值相同的节点.如果满足上述四条性质的二叉树就是二叉查找树.下面我们来一张二叉查找树的图片吧,现在不是都讲究有图有真相吗. 我们还是得从头开始,因为它二叉树是一种数据结构,那么就一定有定义,

ip转发二叉树查找方法c实现

把以前网络课的最长前缀匹配的作业重新写了下.简单说一下,输入输出要求如下,详细要求可见课程页面:https://kattis.csc.kth.se/problem?id=forwarding2 1.路由表以fib <prefix> <interface>格式给出,以换行符结束 2.报文以input <16进制ascii码> 红色为以太报文头,蓝色为目的ip地址 Sample Input fib 10.1.0.0/24 e1 fib 10.2.0.0/24 e2 fib