您的位置:首页 > 其它

for循环里面的递归调用探讨

2016-06-18 22:54 330 查看

for循环里面的递归调用探讨

递归本来要来简化循环问题的,不过程序中往往却有for加递归一起使用的情况。

我们在for里面堪套for,或者for里面的for再堪套for,都能很直观地理解。

当for里面加入了递归,理解的层面就由三维跳到了四维,很难直接观看,要靠无穷的想像力。

先来分析一段小代码

这个代码里,For里面堪套了递归调用,你觉得应该怎样预测它执行的结果呢?

#include <stdio.h>
#include <stdlib.h>

void recur(int, int);
static int count = 0;

int main(int argc, char **argv)
{
if(argc < 2)
{
printf("args need a interger\n");
return -1;
}
int n = atoi(argv[1]);
recur(0, n);
printf("  COUNT= %d\n", count);
return 0;
}

void recur(int i, int n)
{
count++;
printf("B>");
for(i; i <= n; i++){
printf("I>");
recur(i + 1, n);
printf("R>");
}
}


n次的for循环执行的次数是n,n次的递归调用次数亦是n,两者合在一起理想的状态应该是 n2次,但因为递归的特性,却大大增加了复杂度。我们来分析一下代码的执行

1.n=1时程序的输出是: B>I>B>I>B>R>R>I>B>R> COUNT=4

2.即recur调用了4次,和我们预期的结果n2是相同的

3.n=2时,程序输出是:B>I>B>I>B>I>B>R>R>I>B>R>R>I>B>I>B>R>R>I>B>R> COUNT=8

4.这时候,recur调用次数是2n,接着我们再用不同的n测试,也验证了2n的正确性

5.为什么要用for循环调用递归呢?那得由递归解决问题的思路说起,递归解题思路是:

(1)存在一个问题

(2)这个问题可以通过分解形成一个小一点的相同的问题

(3)小一点的问题继续可以分解成更小的问题

(4)最后得出一个最小的问题,最小问题不是问题

例如:计算n的排列的问题,我们可以将它分解成计算(n−1)!,(n−1)!继续分解(n−1−1)!

最后必定会得出一个最小的问题:0! 。

(5)当问题不只一个时,就是说存在一个大的问题,里面有N个同等的中问题,而这N个同等的中问题都可以按递归思路解题,这时候就需要用for来将N个问题遍历了。

(6)为什么for堪套递归的次数是2n?那是因为每一次for都需要调用两次函数,其中for执行一次,for调用递归又需要再执行一次,又根据乘法定理:如果一个过程分成两个阶段,第一阶段有m种可能的结果,并且对于这个阶段的结果,第二阶段都有n种可能的结果,则按指定的次数序完成整个过程一共有mn种方法。这样就可以得出2n。

for调用递归的复杂性不止于此,大家又看看下面的程序,只是要上面的程序里加多一变量,程序的运行复杂度却大大的增加了

#include <stdio.h>
#include <stdlib.h>

void recur(int, int);
static int count = 0;

int main(int argc, char **argv)
{
if(argc < 2)
{
printf("args need a interger\n");
return -1;
}
int n = atoi(argv[1]);
recur(0, n);
printf("  COUNT = %d\n", count);
return 0;
}

void recur(int i, int n)
{
int j;
count++;
printf("B>");
for(j = i; j <= n; j++){   /*这里和上面例子不同之处是将i值赋给j*/
printf("I>");
recur(i + 1, n);
printf("R>");

}
}


当n等于1时,运行的结果的是:

B>I>B>I>B>R>R>I>B>I>B>R>R> COUNT= 5

程序共调用了五次recur函数。

当n等于2时,运行的结果的是:

B>I>B>I>B>I>B>R>R>I>B>I>B>R>R>R>I>B>I>B>I>B>R>R>I>B>I>B>R>R>R>I>B>I>B>I>B>R>R>I>B>I>B>R>R>R> COUNT=16

recur函数的调用一下就升到了16次

当n等于3时,运行的结果的是:

B>I>B>I>B>I>B>I>B>R>R>I>B>I>B>R>R>R>I>B>I>B>I>B>R>R>I>B>I>B>R>R>R>I>B>I>B>I>B>R>R>I>B>I>B>R>R>R>R>I>B>I>B>I>B>I>B>R>R>I>B>I>B>R>R>R>I>B>I>B>I>B>R>R>I>B>I>B>R>R>R>I>B>I>B>I>B>R>R>I>B>I>B>R>R>R>R>I>B>I>B>I>B>I>B>R>R>I>B>I>B>R>R>R>I>B>I>B>I>B>R>R>I>B>I>B>R>R>R>I>B>I>B>I>B>R>R>I>B>I>B>R>R>R>R>I>B>I>B>I>B>I>B>R>R>I>B>I>B>R>R>R>I>B>I>B>I>B>R>R>I>B>I>B>R>R>R>I>B>I>B>I>B>R>R>I>B>I>B>R>R>R>R> COUNT=65

n=3时就更加恐怖了,往下n数值等于10时,等待程序执行结果返回将是一个漫长的过程。

我们来分析一下n=1时的代码执行流程

程序main()调用①recur(i=0, n=1), 打印 B>,说明一下:这里用=代表相等的意思。

第一次进入的for循环记作①for( j=0,i=0, n=1),打印 I> , 开始调用 ②recur(i=1),n值是不变的。

②recur(i=1) 压栈,执行, 打印 B>, 然后进入 ②for (j=1,i=1) ,打印 I>

调用③recur(i=2),打印B>, 到③for (j=2,i=2),空操作,从③recur(i=2)返回

②for (j=1,i=1) 继续下面的print R>, 然后②for (j=2) 空操作,从②recur(i=1)返回

②recur(i=1) 返回①for(j=0,i=0),print R>,然后①for(j=1,i=0),print I,调用④recur(i=1)

④recur(i=1)入栈,print B>, 然后④for(j=i=1), print I, 调用⑤recur(i=2)

⑤recur(i=2)入栈,print B, 然后⑤for(j=i=2)空操作,从⑤recur(i=2)返回,print R

④for(j=i=2)空操作,从④recur(i=1)返回,print R,①for(j=2,i=0),空操作,

从main()返回,结束。

整理一个流程就是:

main()调用①recur(i=0,)->①for( j=0,i=0)调用 ②recur(i=1) ->②for (j=1,i=1)

调用③recur(i=2)->③for (j=2,i=2)从③recur(i=2)返回②for (j=1,i=1)->②for (j++=2) 从②recur(i=1)

返回①for(j=0,i=0)->①for(j=1,i=0) 调用④recur(i=1)->④for(j=i=1)调用⑤recur(i=2)->

⑤for(j=i=2)从⑤recur(i=2)返回④for(j=i=2)从④recur(i=1)返回①for(j=2,i=0)从main()返回

其中要注意的是当①for时当j++时,i值没有变,依然是0, 所它调用recur时是以i=i+1=2这个值调用的

整个过程复杂程度可见一斑,如果n再增加1,我想要非人才能分析清晰。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  递归