什么是递归
1.递归是一种解决问题的方法,在C语言中,就是函数自己调用自己
下面写一个最简单的C语言递归代码
#include<stdio.h>
int main()
{
printf("haha\n");
main();
return 0;
}
运行的结果为
无限打印haha
递归的思想:
把⼀个⼤型复杂问题层层转化为⼀个与原问题相似,但规模较⼩的⼦问题来求解;直到⼦问题不能再被拆分,递归就结束了。所以递归的思考⽅式就是把⼤事化⼩的过程。
递归中的递就是递推的意思,归就是回归的意思。
递归的限制条件
递归在书写的时候是有一定的条件的
条件一:递归存在限制条件,当满⾜这个限制条件的时候,递归便不再继续。
条件二:每次递归调⽤之后越来越接近这个限制条件。
递归的例子举例
1:求n的阶乘
计算n的阶乘(不考虑溢出),n的阶乘就是1~n的数字累积相乘。
稍作分析我们可以得出n的阶乘的递推公式
可以用以下的代码实现
int Fact(int n)
{
if (n == 0)
{
return 1;
}
else
{
return n * Fact(n - 1);
}
}
#include<stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
int r =Fact(n);
printf("%d", r);
return 0;
}
推演过程如图
举例2:顺序打印⼀个整数的每⼀位
输⼊⼀个整数m,打印这个按照顺序打印整数的每⼀位。
⽐如:
输⼊:1234 输出:1 2 3 4
输⼊:520 输出:5 2 0
最低为是最容易得到的,只需要对10去模就行
我们可以写⼀个函数Print来打印n的每⼀位
Print(n)
如果n是1234,那表⽰为
Print(1234) //打印1234的每⼀位
其中1234中的4可以通过%10得到,那么
Print(1234)就可以拆分为两步:
1. Print(1234/10) //打印123的每⼀位
2. printf(1234%10) //打印4
完成上述2步,那就完成了1234每⼀位的打印
那么Print(123)⼜可以拆分为Print(123/10) + printf(123%10)
以此类推
Print(1234)
==>Print(123) + printf(4)
==>Print(12) + printf(3)
==>Print(1) + printf(2)
==>printf(1)
直到被打印的数字变成⼀位数的时候,就不需要再拆分,递归结束。
代码如下
#include<stdio.h>
void Print(int n)
{
if (n > 9)
{
Print(n / 10);
}
printf("%d ", n % 10);
}
int main()
{
int n = 0;
scanf("%d", &n);
Print(n);
return 0;
}
这个问题的思路是:大事化小
我们把Print(1234) 打印1234每⼀位,拆解为⾸先Print(123)打印123的每⼀位,再打印得到的4
把Print(123) 打印123每⼀位,拆解为⾸先Print(12)打印12的每⼀位,再打印得到的3
直到Print打印的是⼀位数,直接打印就⾏。
画图推演
我们发现递归可以用少量的代码进行大量的运算。事实上,我们看到的许多问题是以递归的形式进⾏解释的,这只是因为它⽐⾮递归的形式更加清晰,
但是这些问题的迭代实现往往⽐递归实现效率更⾼。
当⼀个问题⾮常复杂,难以使⽤迭代的⽅式实现时,此时递归实现的简洁性便可以补偿它所带来的运⾏时开销。
递归与迭代
递归是⼀种很好的编程技巧,但是很多技巧⼀样,也是可能被误⽤的。
比如以下的例子:
求第n个斐波那契数
看到这公式我们很容易联想到递归,容易写出以下的代码
int Fib(int n)
{
if (n <= 2)
{
return 1;
}
return Fib(n - 1) + Fib(n - 2);
}
#include<stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
int ret =Fib(n);
printf("%d", ret);
return 0;
}
我们发现当我们输入50时代码需要很长的时间去执行
当代码终于得出结果时我们发现
结果是错的,这是因为在递归的过程中会有重复计算,⽽且递归层次越深,冗余计算就会越多。我们可以写个代码测试一下
int count = 0;
int Fib(int n)
{
if (n == 3)
count++;//统计第3个斐波那契数被计算的次数
if (n <= 2)
{
return 1;
}
return Fib(n - 1) + Fib(n - 2);
}
#include<stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
int ret =Fib(n);
printf("%d\n", ret);
printf("count=%d", count);
return 0;
}
我们发现当我们找第40个斐波那契数的时候重复很多次的运算,所以程序耗费的时间才久的。所以斐波那契数的计算,使⽤递归是⾮常不明智的,我们就得想迭代的⽅式解决。
我们知道斐波那契数的前2个数都1,然后前2个数相加就是第3个数,那么我们从前往后,从⼩到⼤计算就⾏了。
这样就有下⾯的代码
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;
}
迭代的⽅式去实现这个代码,效率就要⾼出很多了。
有时候,递归虽好,但是也会引⼊⼀些问题,所以我们⼀定不要迷恋递归,适可⽽⽌就好。递归虽好,可不要贪杯。