1.什么是递归
递归是一种解决问题的方法,在C语言中,递归就是函数自己调用自己。
递归的思想:把一个大型复杂的问题层层转化为一个与原问题相似,但规模较小的问题来求解;知道子问题不能再被拆分,递归就结束了。所以递归的思考方式就是大事化小的过程。
递归中,递是递推的意思,归是回归的意思。
递归在书写规程中有两个必要条件:
(1)递归存在限制条件,当满足这个限制条件时,递归便不再继续。
(2)递归调用之后越来越接近这个限制条件。
2.递归举例
下面我们通过几个递归例子来学习体会递归的巧妙。
2.1求n的阶乘
我们知道n的阶乘的公式: n!= n∗(n−1)!当n==0时,n的阶乘是1,由此我们可以得到
1,n=0
递归公式fact(n) =
n*fact(n-1) ,n>0
那我们就可以写出函数Fact求n的阶乘,假设Fact(n)就是求n的阶乘,那么Fact(n-1)就是求n-1的阶 乘,函数如下:
int fact(int a)
{
if (a == 0)
{
return 1;
}
else
{
return a*fact(a - 1);
}
}
以下是完整代码:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
//n的阶乘
int fact(int a)
{
if (a == 0)
{
return 1;
}
else
{
return a*fact(a - 1);
}
}
int main()
{
int n = 0;
printf("请输入你想计算的数:");
scanf("%d", &n);
int sum = fact(n);
printf("n的阶乘是%d", sum);
return 0;
}
画图推演:
假如我们计算5的阶乘,输入5,
fact(5)=5*fact(4) fact(5)=120
fact(4)=4*fact(3) fact(4)=24
递 fact(3)=3*fact(2) fact(3)=6 归
推 fact(2)=2*fact(1) fact(2)=2 回
fact(1)=1*fact(0) fact(1)=1
fact(0)=1
2.2顺序打印一个整数的每一位
假如我们输入1234,1234%10就能得到4,然后1234/10得到123,这就相当于去掉了4 然后继续对123%10,就得到了3,再除10去掉3,以此类推不断的%10和/10操作,直到1234的每⼀位都得到,但是这里有个问题就是得到的数字顺序是倒着的。那么我们就可以通过递推的递推和回归思想来顺序打印我们想要的结果。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void print(int n)
{
if (n > 9)
{
print(n / 10);
}
printf("%d ", n % 10);
}
int main()
{
int x = 0;
printf("请输入要打印的数字:");
scanf("%d", &x);
print(x);
return 0;
}
递推: print(1234)n>9 ("/10; print(123) n>9 /10; print(12) n>9 /10; print(1)
回归打印 。
3.递推和迭代
递推可以产⽣正确的结果,但是在递归函数调用的过程中涉及⼀些运行,递推层次太深,会浪费太多栈帧空间,也可能引起栈溢出的问题。所以如果不想使用递归就得想其他的办法,通常就是迭代的方式(通常就是循环的方式)。
比如:计算n的阶乘,也是可以产⽣1~n的数字累计乘在⼀起的。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int fact(int a)
{
int b = 0;
int c = 1;
for (b = 1; b <= a; b++)
{
c *= b;
}
return c;
}
int main()
{
int x = 0;
scanf("%d", &x);
int y = fact(x);
printf("%d", y);
return 0;
}
上述代码能够完成任务,并且效率更高。
下面我们在举一个不适合用递归的例子:求n个斐波那契数
使用递归描述:Fib(n) =1 ,n<=2
=Fib(n-1) + Fib(n-2) ,n>2
此公式很容易诱导我们写成递归的形式,如下所示
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int Fib(int n)
{
if (n <= 2)
{
return 1;
}
else
{
return Fib(n - 1) + Fib(n - 2);
}
}
int main()
{
int a = 0;
scanf("%d", &a);
int b = Fib(a);
printf("%d", b);
return 0;
}
当我们输入一个较大的数字,比如50时,需要很长时间才能算出来,因为当计算Fib(50)时要计算Fib(49)和Fib(48),而计算Fib(49)要计算Fib(48)和Fib(47),而计算Fib(48)又要计计算一次 Fib(48)和Fib(47),以此类推,可见,要计算50的斐波那契数需要多大的计算量,所以用递归的方法计算斐波那契数时很低效的。
但是用迭代的方法去实现这个代码,效率就高多了。我们知道斐波那契数的前2个数都1,然后前2个数相加就是第3个数,那么我们从前往后,从小计算到大就行了。
int Fib(int n)
{
int x = 1;
int y = 1;
int z = 1;
while (n > 2)
{
z = x + y;
x = y;
y = z;
n--;
}
return z;
}
事实上,我们看到的许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更加清晰, 但是这些问题的迭代实现往往比递归实现效率更⾼。 当⼀个问题非常复杂,难以使用迭代的方式实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。