1、什么是递归?
递归是一种问题解决方法,其实就是函数自己调用自己。代码如下所示:
#include <stdio.h>
int main()
{
printf("hehe\n");
main();//main函数自己又调用了自己
return 0;
}
上诉只是一个演示案例,运行了代码会由于死循环导致栈溢出(Stack overflow)。
1.1递归的思想
把一个复杂的大型问题层层转化为与原问题相似,但规模较小的子问题来进行求解;直到子问题不能再被拆分,递归就结束,所以递归就是一个把大事化小的一个过程。
1.2递归的限制条件
为了防止递归进入死循环,导致栈溢出,为此递归函数需要加入2个必要条件。
(1)递归存在限制条件,当满足这个限制条件时,递归就不再继续。
(2)每次递归后都会越来越接近这个限制条件。
2、递归举例
2.1求n的阶乘
题目:计算出n的阶乘,就是从1~n的数字累积相乘。
2.1.1代码分析和实现
阶乘公式:n!=n*(n-1)!
举例:
5!=5*4*3*2*1
4!=4*3*2*1
所以:
5!=5*4!
这样就是把复杂问题,层层化为相似的简单问题进行求解。
当n==0时,n!的阶乘为1,剩下的都可以用公式计算。
阶乘公式如下所示:
那我们就可以写出函数Fact求n的阶乘,假设Fact(n)就是求n的阶乘,那么Fact(n-1)就是求n-1的阶 乘,代码如下:
#include <stdio.h>
int Fact(int n)
{
if(n==0)
return 1;
else
return n*Fact(n-1);
}
int main()
{
int n = 0;
scanf("%d",&n);
int ret=Fact();
printf("%d",ret);
return 0;
}
运行结果:
3、递归与迭代
递归时一种很好的编程技巧,但有时会被误用。因为在C语言中每一次的函数调用,都需要为本次函数调用在内存的栈区,申请一块内存空间用来保存函数调用期间使用到的各种局部变量的值,这部分被称为运行时堆栈,或函数栈帧。
函数不反回,函数对应的栈帧空间就一直被占用,所以如果函数调用中存在递归调用的话,每一次函数调用都会开辟自己的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间。
所以采用函数递归的方式编写代码,如果递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢出的问题。
所以不想使用递归就需要用其他的方法,通常是迭代的方式(通常是循环的方式)。
n的阶乘的问题就可以用迭代的代码如下所示:
itn Fact(int n)
{
int i = 0 ;
int ret = 0;
for(i=0;i<=n;i++)
{
ret*=i;
}
return ret;
}
上述代码也是能够完成任务,而且效率还要比递归的方式要高。
事实上,我们看到的许多问题是以递归的形式进⾏解释的,这只是因为它⽐⾮递归的形式更加清晰, 但是这些问题的迭代实现往往⽐递归实现效率更⾼。 当⼀个问题⾮常复杂,难以使⽤迭代的⽅式实现时,此时递归实现的简洁性便可以补偿它所带来的运⾏时开销。
3.1 求第n个斐波那契数
公式如下:
看到这个公式很容易诱导我们将代码写成递归的方式,如下所示:
#include <stdio.h>
int Fib(int n)
{
if(n<=2)
return 1;
else
retrun Fib(n-1)+Fib(n-2);
}
int main()
{
int n = 0;
scanf("%d",&n);
int ret = Fib(n);
printf("%d",ret);
return 0;
}
当我们n输⼊为50的时候,需要很⻓时间才能算出结果,这个计算所花费的时间,是我们很难接受的,其实递归程序会不断的展开,在展开的过程中,我们很容易就能发现,在递归的过程中会有重复计 算,⽽且递归层次越深,冗余计算就会越多。
因此我们就会选用迭代的代码方式,如下所示:
int Fib(int n)
{
int a = 1;
int b = 1;
int c = 1;
while(n>2)
{
c=a+b;
a=b;
b=c;
n--;
}
return c;
}
迭代的方式,效率就会高很多。