1.递归的思想
把一个大型复杂问题层层转化为一个与原问题相似,但规模较小的子问题来求解;直到子问题不能再被拆分,递归就结束了。所以递归的思考方式就是把大事化小的过程。
递归这两个字也有重要的意义:
递归中的递就是递推的意思,归就是回归的意思。
2.递归的限制条件
我们可以先看一段代码。
#include<stdio.h>
int main()
{
printf("hello\n");
main();
return 0;
}
我们可以看到在打印完hello后会再次调用main()函数,这样无限制的调用,就会导致程序陷入死循环。所以对于函数的递归我们需要一定的限制条件。
递归在书写的时候,有2个必要条件:
- 递归存在限制条件,当满足这个限制条件的时候,递归便不再继续。
- 每次递归调用之后越来越接近这个限制条件。
3.函数递归的优缺点
优点:
函数递归只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的主要思考方式在于:把大事化小 。
缺点:①如果函数递归使用不恰当,会导致栈溢出,因为每一次函数调用都会在栈区上申请内存空间。
②每一次函数递归(函数调用)都会在函数栈帧上开辟一块空间,所谓的压栈。这样会大大降低我们代码的执行效率。
4. 递归举例
求n的阶乘
我们知道一个正整数的阶乘是所有小于及等于该数的正整数的积,并且0的阶乘为1。 自然数n的阶乘写作n!。
例如:
5!=5*4*3*2*1
4!=4*3*2*1
那么5!我们是不是也可看出成这样:
5!=5!*4!
这样我们就可得到一个式子:n!=n*(n-1)!。我们是不是可以利用递归的思想结合这个式子来实现n的阶乘 。
#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(n);
printf("%d的阶乘为:%d", n, ret);
return 0;
让我来解释一下这个式子吧!
当我们输入一个数,程序就会将数放入Fact()函数里,调用函数,当我进入函数里,如果我们输入的数为0就会返回1(0!=1),否则就会执行else,在else里回=会执行n*Fact (n-1);我们可以看到有Fact()函数,所以我们会再次调用函数,其实我们就可以把式子看成
n*(n-1)*Fact(n-1-1),然后再次调用函数。
我们也可以看一下下面这个图(红线代表递推,蓝线代表回归):
5.递归与迭代
递归不一定优于非递归,递归也是有一定的缺点的。
我们可以举个例子:求第n个斐波那契数
斐波那契数: 1 1 2 3 5 8 13 21 34 55.....其实就是前两个数相加等于下一个数。
如果我们用递归的方式来实现
#include<stdio.h>
int Fib(int n)
{
if (n <= 2)
return 1;
else
return Fib(n - 1) + Fib(n - 2);
}
int main()
{
int n = 0;
scanf("%d", &n);
int c = Fib(n);
printf("%d", c);
}
可以看到当n<=2是就返回1;n>2时 ,就会利用递归来实现,计算第n个数时就只需要计算他前面的两个数的值再相加
,但是当数过大时,我们就会发现,计算的次数太多了,而且还会重复计算,这样代码的效率就会大大下降。但如果我们用非递归的方式来计算
#include<stdio.h>
int Fib(int n)
{
int a = 1;
int b = 1;
int r = 0;
if (n <= 2)
return 1;
while (n > 2)
{
r = a + b;
a = b;
b = r;
n--;
}
return r;
}
int main()
{
int n = 0;
scanf("%d", &n);
int c = Fib(n);
printf("%d", c);
return 0;
}
我们可以看到,我们只需要利用循环将前两个数相加,在重新将前两数的值改变(将第一个数的值变为第二个数的,将第二个数的值变为前两个数相加的值)对于循环的条件,从斐波那契数来看
1 1 2 3 5 8 13 21 34 55...,第三个数需要加一次(1+1)第四个数需要加两次(1+1=2,1+2=3)
所以只需要n>2,循环一次n--;就可以啦。这样我们可以看到非递归的方式求斐波那契数,的速率就比递归的快很多了。
好了,今天就分享到这里了,还请大家多多关注,我们下一篇见!