以菲波那切数列为例:
菲波那切数列可以表示为:1,1,2,3,5,8,13。。。。。
写出菲波那切数列for循环的代码:
#include <stdio.h>
int Fibon(int n)
{
int f1 = 1;
int f2 = 1;
int fn;
if(n<3)
{
return 1;
}
for(int i=1;i<n;i++)
{
fn = f1 + f2;
f1 = f2;
f2 = fn;
}
return fn;
}
int main()
{
printf("%d\n",Fibon(1));
printf("%d\n",Fibon(2));
printf("%d\n",Fibon(3));
printf("%d\n",Fibon(4));
printf("%d\n",Fibon(5));
printf("%d\n",Fibon(6));
}
通过观察发现,菲波那切数列也可以表示为:f(n)=f(n-1)+f(n-2)。
如果用上面的公式求出斐波那契数列,代码应为:
int Fibon(int n)
{
int tmp;
if(n==1||n==2)
{
return 1;
}
else
{
/* error:return f(n)=f(n-1)+f(n-2); //注意此处不能用,因为f是未定义的标识符。*/
/*error: return Fibon(n)=Fibon(n-1)+Fibon(n-2);//表达式必须是能修改的左值*/
① return tmp=Fibon(n-1)+Fibon(n-2);//可以定义一个形参,来保存f(n)的值。
//也可以考虑不用形参的形式,如:
② return Fibon(n-1)+Fibon(n-2);
}
}
int main()
{
printf("%d\n",Fibon(1));
printf("%d\n",Fibon(2));
printf("%d\n",Fibon(3));
printf("%d\n",Fibon(4));
printf("%d\n",Fibon(5));
printf("%d\n",Fibon(6));
}
例如方法二中的,在函数调用中,如果直接或者间接的调用函数本身,我们叫做递归调用。递归调用有时也被称为循环定义。
例如:在菲波那切数列的代码中,我们定义了int Fibon(int n),求出结果时,我们又用了 return Fibon(n-1)+Fibon(n-2)。这就是函数调用最明显的标志。但现实中并不推荐使用递归来实现菲波那切数列,这也是递归的缺点。
递归的缺点主要有以下几个方面:
1.递归由于是函数调用自身,而函数调用是有时间和空间的消耗的:每一次函数调用,都需要在内存栈中分配空间以保存参数、返回地址以及临时变量,而往栈中压入数据和弹出数据都需要时间。->效率
2.递归中很多计算都是重复的,由于其本质是把一个问题分解成两个或者多个小问题,多个小问题存在相互重叠的部分,则存在重复计算,如fibonacci斐波那契数列的递归实现。->效率
3.调用栈可能会溢出,其实每一次函数调用会在内存栈中分配空间,而每个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。->性能
类似的例子还有很多,例如:
求n![n!=(n-1)!*n]:
int Num(int n)
{
if(n==0||n==1)//0的阶乘是1
{
return 1;
}
else
{
return Num(n-1)*n;
}
}
int main()
{
printf("%d\n",Num(0));
printf("%d\n",Num(1));
printf("%d\n",Num(2));
printf("%d\n",Num(5));
}
求和[f(n)=f(n-1)+n]:
int sum(int n)
{
if(n==0)
{
return 0;
}
else
{
return sum(n-1)+n;
}
}
int main()
{
printf("%d\n",sum(0));
printf("%d\n",sum(1));
printf("%d\n",sum(5));
printf("%d\n",sum(10));
}
从上面的代码,我们不难看出,递归通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描绘出解题过程所需要的多次重复计算,大大减少了程序的代码量。
递归的能力在于用有限的语句来定义对象的无限集合。一般来说,递归需要有边界条件-----用来缩小规模还要有递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。