递归剖析及应用

1.1.1           概念

一些操作可以分解为很多相同的更小的操作,这个时候我们可以使用递归,一般来说,这样的操作也可以使用迭代完成,但是递归可能更简洁。

递归的难点在于,递归要一直在微观和宏观之间切换,这会造成一些初学者很难找到里面的规律。为了说明白刚才这句话,我们做以下几个定义:

整体 – 要处理的对象,如二叉树

单元 – 分解为的最小操作对象,如节点

余部 – 由单元组成的部分对象集合,它是和整体一个层面的概念

 

整体 = 单元 + 余部*N

 

如遍历二叉树,即为根节点+ 遍历左右子树,根节点在这就是单元,而左右子树就是2个余部,子树和二叉树都是树。所以,余部和整体是一个层面的概念,是宏观的;而单元和整体与余部不是一个层面的概念,是微观的。

1.1.2           递归结构

我们来看下中序遍历二叉树的代码

voidBinTree::walk_in(BinTreeNode* node) const{

         if(node == NULL)

                   return;

        

         walk_in(node->get_left());

         printf("%d\n", node->get_data());

         walk_in(node->get_right());

}

单元是节点,printf("%d\n",node->get_data())是对单元的操作,对单元的操作我们很容易理解,对每个单元都进行这样的操作,那么整体就被处理完了。

比较难理解的是对余部的处理,我们首先怎么理解余部呢。我是这样来理解的,余部对我们来说像是个黑盒子的,它总能按照我们预先设定好的要求完成它该完成的功能,我们就像调用库函数一样调用它们。如上,walk_in(node->get_left())walk_in(node->get_right()),我们认为它肯定能够按照中序遍历左子树和右子树。所以,只要遍历的根节点和根节点的两个子树,整个树就遍历完了。递归的特点就是调用自己,而初学者往往在这个问题上纠结,造成逻辑混乱,要将调用自己和调用其他函数一样看待即可。

说到这,我们其实应该可以感觉到迭代和递归的区别了,迭代是将整体分解为节点,然后循环处理每个节点;而递归是将整体分解为一个节点和N个余部,然后处理这个节点,并且获得N个余部的结果(注意我们没有参与余部的处理,只是获得它的结果),最后将结果进行合并处理。

到目前为止,我们应该可以总结出完成一个递归需要的几个步骤。另外需要补充的是,如果更容易理解递归的逻辑,有时可以将处理的单元想象成第一个要处理的单元,如二叉树的根节点,处理其他节点和处理根节点一样。

1.1.3           步骤

1.      根据处理思路将整体分解为单元和对应的余部(不同处理思路,分解方法可能不一样)

2.      设定余部处理函数(也就是递归函数本身)的输入和输出

3.      合并单元处理结果和余部结果

另外,我们在开始的地方要设置一个阀值,指定在什么情况下,节点就已经处理完了,或者就没必要再处理余下的节点了。注意,我们

         我们以求阶乘n!为例,按照以上的步骤进行处理。

分解

         我们的处理思路是要求N的阶乘,只要求出n-1的阶乘然后乘上n就可以得到n的阶乘。所以我们的单元为每个整数,而余部就一个就是单元后面所有整数的乘积。

函数输入输出

         要求余部的阶乘,只要将余部开头的整数传递过来即可,所以输入为余部的开头整数,输出为余部的阶乘结果。而这个余部处理函数也就是递归函数的输入输出。定义函数如下:

         intfunc(int n){

}

由第一步的分解分析,得到下面的程序

int func(int n){

         //n:单元,在这单元不需要处理

         //余部处理函数为func(n-1)

         Int tmp = func(n-1);//处理余部

}

合并

         将当前单元的值和单元后面的阶乘相乘即可。更容易理解的是,只要将最大值N和N-1的阶乘相乘就是N的阶乘。合并结果如下

int func(int n){

         //n:单元,在这单元不需要处理

         //余部处理函数为func(n-1)

         Int tmp = func(n-1);//处理余部

         return n* tmp;//合并处理结果和单元

}

阀值

         当传递进来的值等于0时,说明单元已经处理完了。得到如下程序

int func(int n){

         //设定阀值

         if(n==0)

                   return1;

 

         //n:单元,在这单元不需要处理

         //余部处理函数为func(n-1)

         Int tmp = func(n-1);//处理余部

         return n* tmp;//合并处理结果和单元

}

现在这个程序基本框架就已经写完了,当然函数也有很多需要优化和精简的地方,最后如下

int func(int n){

         if(n==1)

                   return 1;

                  return n* func(n-1);

}

1.1.4           总结

哎,总觉得自己想得特别明白,但是自己写出来的却没那么明白,当一个作家确实不容易啊。如果有谁将我的思路总结得更好或者有更好的其他思路,希望能够进行进一步的剖析,当然别忘了告诉我更好的剖析思路的地址。

你可能感兴趣的:(递归)