递归和递推都是算法设计中的难点,算法又十分相近,很多和我一样学生误认为是一回事,非常容易混淆。其实它们之间既有相似点,又有明显的区别。
递推一般用循环来解决,从已知条件到未知逐渐接近结果;
(1)将复杂运算分解为若干重复的简单运算
(2)后一步骤建立在前一步骤之上
(3)计算每一步骤的方法相同
(4)从开始向后计算出结果
(5)使用循环结构,通过多次循环逐渐逼近结果
递归一般自己调用自己,从未知到已知,把规模大的、较难解决的问题变成规模较小的、易解决的同一问题。规模较小的问题又变成规模更小的问题,并且小到一定程度可以直接得出它的解,从而得到原来问题的解。
(1)每一次递归都缩小问题规模,直到问题足够小
(2)使用选择分支语句
(3)从后往开始逐步逼近
(4)达到最开始,再把初始值带入往后逐一求解
下面通过例子逐个介绍。
递推:
一。 解阶乘 n! = 1*2*3*4*....*(n-1)*n.
- fun(int n)
- {
- long s=1;
- int i;
- for(i=1;i<=n;i++)
- s=s*i;
- return s;
- }
二。捕鱼问题
A,B,C,D,E五个渔夫夜间合伙捕鱼,,第二天清A先醒来,他把鱼均分五份,把多余的一条扔回湖中,便拿了自己的一份回家了,B醒来后,也把鱼均分五份,把多余的一条扔回湖中,便拿了自己的一份回家了,C,D,E也按同样方法分鱼。问5人至少捕到多少条鱼?
这也是一个递推问题,递推关系式为 F(n+1) = F(n)*5/4+1 (i = 1,2,3,4) F(5)即捕鱼的总数
- #include <stdio.h>
- int main()
- {
- int i, n, f[5], flag;
- flag = 1;
- n = 1;
- while (flag == 1)
- {
- f[0] = 5*n+1;
- flag = 0;
- for (i=1; i<5; i++)
- {
- if (f[i-1]%4!=0)
- {
- flag=1;
- break;
- }
- f[i] = 5*f[i-1]/4+1;
- }
- n++;
- }
- printf("%d\n",f[4]);
- return 0;
- }
三。平面分割
- #include <stdio.h>
- int main()
- {
- int i, n, f2, f1;
- while (1)
- {
- scanf("%d",&n);
- if (n==0)
- {
- break;
- }
- f1 = 2;
- for (i=1; i<=n; i++)
- {
- f2 = f1+2*(i-1);
- f1 = f2;
- }
- printf("%d\n",f2);
- }
- return 0;
- }
递归:
一。计算组合数
问题:计算组合数C(10,3)(组合数:从n个不同元素中,任取m(m≤n)个元素并成一组,叫做从n个不同元素中取出m个元素的一个组合;从n个不同元素中取出m(m≤n)个元素的所有组合的个数,叫做从n个不同元素中取出m个元素的组合数。)
我们可以利用组合恒等式:
- #include <stdio.h>
- int Cmn(int m, int n )
- {
- if (m<0 || m<n || n<0)
- {
- return 0;
- }
- if (m==n) {
- return 1;
- }
- if (n==1)
- {
- return m;
- }
- return Cmn(m-1,n)+Cmn(m-1,n-1);
- }
- int main()
- {
- printf("C(10,3) = %d \n",Cmn(10,3));
- return 0;
- }
- <span style="font-size:14px;">long ff(int n)
- {
- long f;
- if(n<0) printf("n<0,input error");
- else if(n==0||n==1) f=1;
- else f=ff(n-1)*n;
- return(f);
- }
- main()
- {
- int n;
- long y;
- printf("\ninput a inteager number:\n");
- scanf("%d",&n);
- y=ff(n);
- printf("%d!=%ld",n,y);
- }</span>
三。Hanoi 汉诺塔问题
本题算法分析如下,设A上有3个盘子:
1将A上的2个圆盘移到B
2将A上的1个圆盘直接移到C
3将B上的2个圆盘移到C其中第2步可以直接实现
将第1步分解:(借助C)
1.1
1.2
1.3
将第3步分解:(借助A)
3.1
3.2
3.3
由此可知,将3个圆盘从A移到C,需要7步,将n个圆盘从A移到C需要2的n次方减1步,要进行第n步,需要先进行第n-1步,可分为3个步骤
1将A上n-1个圆盘借助C移到B
2将A上剩下的1个圆盘直接移到C
3将B上n-1个圆盘借助A移到C
- <pre name="code" class="cpp">#include <stdio.h>
- void Move(int n,char a,char b)
- {
- printf("%d:%c-->%c\n",n,a,b);
- }
- void Hanoi(int n,char a,char b,char c)
- {
- if(n==1)//将A上的1个圆盘直接移到C,递归退出条件
- Move(n,a,c);
- else
- {
- Hanoi(n-1,a,c,b);//将A上n-1个圆盘借助C移到B
- Move(n,a,c);//将A上剩下的1个圆盘直接移到C
- Hanoi(n-1,b,a,c);//将B上n-1个圆盘借助A移到C
- }
- }
- int main()
- {
- int n;
- printf("\ninput number:");
- scanf("%d",&n);
- Hanoi(n,'A','B','C');
- return 0;
- }
- /* 打印结果
- input number:3
- 1:A-->C
- 2:A-->B
- 1:C-->B
- 3:A-->C
- 1:B-->A
- 2:B-->C
- 1:A-->C
- */</pre><br>
- <br>
- <pre></pre>
- <br>
下面通过一个具体例子比较一下递归和递推。
- long f(int n)
- {
- if(n<=2)
- return 1;
- else
- return f(n-1)+f(n-2);
- }
递推解:
- long f(int n)
- {
- long t;f1=1,f2=1;
- if(n<=2)
- reutrn 1;
- for(int i=3;i<=n;i++)
- {
- t=f1+f2;
- f1=f2;
- f2=t;
- }
- }
由此可见,递归重在“归”,你要求的是什么,就直接求什么,至于要怎么去调用、怎么去求,让函数自己一步一步去反复调用;递推重在“推”,是按照我们平时做数学的方法来算。编程时递归比较特殊, 表现 在函数自身调用自身,这样编程代码紧凑,程序的可读性好,但效率低,还可能导致堆栈溢出,特别是递归层次比较多时。一般来说,递归程序都可以用循环程序实现,用循环程序实现虽然较难理解,但 安全 可靠。