递归和递推都是算法设计中的难点,算法又十分相近,很多和我一样学生误认为是一回事,非常容易混淆。其实它们之间既有相似点,又有明显的区别。
递推一般用循环来解决,从已知条件到未知逐渐接近结果;
(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;
}
二。 解阶乘 n! = 1*2*3*4*....*(n-1)*n.
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);
}
三。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
#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 */
下面通过一个具体例子比较一下递归和递推。
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;
}
}
由此可见,递归重在“归”,你要求的是什么,就直接求什么,至于要怎么去调用、怎么去求,让函数自己一步一步去反复调用;递推重在“推”,是按照我们平时做数学的方法来算。编程时递归比较特殊, 表现 在函数自身调用自身,这样编程代码紧凑,程序的可读性好,但效率低,还可能导致堆栈溢出,特别是递归层次比较多时。一般来说,递归程序都可以用循环程序实现,用循环程序实现虽然较难理解,但 安全 可靠。