1 递归是什么
在c语言中,递归简单来说就是函数自己调用自己。
递归中递就是递推的意思,归就是回归的意思,递归就是要把大事化小,把困难的问题分解成规模较小的小问题来解决。
最简单的调用函数:
#include <stdio.h>
int main()
{
printf("hehe");
main();
return 0;
}
当程序运行进入main函数打印hehe后,调用main函数,再次打印hehe,一直重复这样的操作,最终我们会发现,程序会死递归,导致栈溢出(程序运行时使用的栈空间超出了为其分配的大小,导致程序异常终止)。
举例:求n的阶乘
我们知道n的阶乘的公式:n!=n*(n-1)!
这个公式的思路就是把一个大的问题转换成与原问题类似的,但规模较小的问题来解决的。
例如当n=5的时候,
5!=5*(4!)=5* 4* (3!)=5* 4* 3* (2!)=5* 4* 3* 2* (1!)=5* 4* 3* 2* 1
代码如下:
#include <stdio.h>
int Fac(int n)
{
if (n > 0)
return Fac(n - 1) * n;
else
return 1;
}
int main()
{
int n;
scanf("%d", &n); //n的值不易过大,否则结果会超出整形能表示的最大值
int ret = Fac(n);
printf("%d", ret);
return 0;
}
我们能通过画图更直观的清楚递推和回归的过程。(求5的阶乘)
2 递归的两个重要的限制条件
• 递归需要一个限制条件,当程序满足限制条件的时候,递归便结束。(例如if语句进行判断)
• 每次递归调用的时候都要逐渐接近这个限制条件。(例如递归的值朝着限制条件一直增大或减小)
3 递归与迭代
在c语言中,每使用一次函数调用,都要在栈区为函数开辟一块内存空间,函数如果不返回,其空间会一直占用。也就是说,如果递归的层次太深,一直不返回,会导致浪费栈空间,也可能导致栈溢出。
我们平时看到的一些问题用递归的方式来解决思路很简单,但是在实现时会有很多问题,因此,我们可以考虑其他的方法,例如使用迭代(循环)的方法,迭代在实现一些问题的时候往往比递归的效率更高。
我们知道斐波那契数列是指这样一个数列:
1,1,2,3,5,8,13,21,34,55,89……这个数列从第3项开始 ,每一项都等于前两项之和。
题目:求斐波那契数列的第n个数。
在知道了斐波那契的公式后,我们很容易就会想到递归的方法。
#include <stdio.h>
int Fib(int n)
{
if (n > 2)
return Fib(n - 1) + Fib(n - 2); //返回前两项之和
//调用时n的值变小了,满足递归的第二个限制条件。
else
return 1; //n=1和n=2时斐波那契数列都是1
}
int main()
{
int n;
scanf("%d", &n);
int ret = Fib(n);
printf("%d ", ret);
return 0;
}
当我们输入大一点的数,例如50的时候,我们会发现程序会运行很长时间,然后给出一个错误答案,这是为什么呢?
通过画图我们可以发现,当输入一个较大的数时,越小的数被计算的次数越多。
我们可以尝试统计斐波那契前面的数被计算的次数。
#include <stdio.h>
int count = 0;
int Fib(int n)
{
if (n == 4)
count++;
if (n > 2)
return Fib(n - 1) + Fib(n - 2);
else
return 1;
}
int main()
{
int n;
scanf("%d", &n);
int ret = Fib(n);
printf("ret = %d ", ret);
printf("count = %d ", count);
return 0;
}
可以发现,当输入的数为50时,第三个斐波那契数被计算了很多次(太大了,超出int的最大范围),这会导致计算冗余,代码效率十分的低下。
因此我们可以考虑用迭代来实现求第n个斐波那契数。
1.因为斐波那契数前两个值就是1,不是相加得到的,我们可以定义变量a和b来存放1.
2.从第三个数开始,遵循每一个数等于前两个数之和的规律,所以我们可以定义变量c来保存前两个数之和的结果。
3.当每往后求一个斐波那契数,我们可以把a,b,c的位置都往后移一个位置,此时a的值为之前b的值,b的值为之前c的值。
代码如下:
#include <stdio.h>
int Fib(int n)
{
int a = 1;
int b = 1;
int c = 1; //因为当n小于2时不进入while循环,直接返回c的值
//前两个数都为1,所以可以把c的初始值赋值为1.
while (n>2) //只有当n>2时,满足规律
{
c = a + b;
a = b;
b = c;
n--;//可以把n理解为次数,每次把a,b,c往后移一位后,移位的次数要减少。
}
return c;
}
int main()
{
int n;
scanf("%d", &n);
int c = Fib(n);
printf("c = %d ", c);
return 0;
}
虽然程序的结果依然是错的(超出了整形能表示的最大值),但是程序一下子就出结果了,程序效率高了很多。
递归和迭代都有适用的题目,在解决比较困难的问题时,我们可以先尝试用迭代来解决,如果题目过于复杂,迭代无法解决,可以再考虑使用递归。