1.递归
1.1递归的思想
把一个大型复杂问题层层转化为一个与原问题相似,但规模较小的子问题来求解,知道子问题不能再被拆分,递归就结束了。所以递归的思考方式就是把大事化小的过程。
递归中的递就是递推的意思,归就是回归的意思。
1.2递归的限制条件
递归在书写的时候,有两个必要条件;
1.递归存在限制条件,当满足这个限制条件的时候,递归便不再继续。
2.每次递归调用结束之后会越来越接近这个限制条件。
在下面的例子中,我们逐步体会这两个限制条件。
1.2.1举栗1
以求某个数的阶乘为例。
我们知道n的阶乘的公式:
n
!
=
n
∗ (
n
− 1)!
比如5!=5*4*3*2*1
4!=4*3*2*1
就可以知道5!=5*4!
这样就把大的问题转换成为小的问题,这就是递归的思路,同时我们要有一个限制条件。那就是当n==0的时候,阶乘等于1,此时递归结束,这就是一个完整的递归思路。
用代码表示
#include<stdio.h>
int Fact(int n)
{
if (n == 0)
return 1;
if (n > 0)
return n * Fact(n - 1);
}
int main()
{
printf("%d", Fact(3));
return 0;
}
用图像表示
![](https://i-blog.csdnimg.cn/blog_migrate/0a068434f33a6c3b60ed7750a2d46b65.png)
通过图示我们可以清晰的看到这条递归所包含的两个限制条件。
1.2.2举栗2
顺序打印一个数的每一位
先思考,假如n是一位数,那么打印出来的结果就是他本身,那么如果n是多位数,我们怎么获取他的每一位呢?
例如n=1234,n%10=4,n/10=123,接下去123%10=3,123/10=12,。以此类推就可以得到每一位,当n只剩下一位数的时候结束循环,这就是大事化小的思路,同时也完成了递归的条件。
图像思路就是这样。
思路有了代码也能很容易的写出来。
#include<stdio.h>
void Print(int n)
{
if (n > 9)
{
Print(n / 10);
}
printf("%d ", n % 10);
}
int main()
{
int m = 0;
scanf_s("%d", &m);
Print(m);
return 0;
}
2.递归与迭代
从刚才的学习中我们可以知道,
递归是⼀种很好的编程技巧,但是很多技巧⼀样,也是可能被误⽤的,就像举例1⼀样,看到推导的公
式,很容易就被写成递归的形式:
![](https://i-blog.csdnimg.cn/blog_migrate/d4f848db1e660298d52072d5c078a4f5.png)
#include<stdio.h>
int fact(int n)
{
if (n == 0)
return 1;
if (n > 0)
return n * fact(n - 1);
}
int main()
{
printf("%d", fact(3));
return 0;
}
这个例子乍一看很适合用递归的方式去解决,但是
在C语⾔中每⼀次函数调⽤,都要需要为本次函数调⽤在栈区申请⼀块内存空间来保存函数调⽤期间 的各种局部变量的值,这块空间被称为运⾏时堆栈,或者函数栈帧。
函数不返回,函数对应的栈帧空间就⼀直占⽤,所以如果函数调⽤中存在递归调⽤的话,每⼀次递归
函数调⽤都会开辟属于⾃⼰的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间。
所以如果采⽤函数递归的⽅式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢 出(stack overflow)的问题
所以如果不想使⽤递归就得想其他的办法,通常就是迭代的⽅式(通常就是循环的⽅式)。
int Fact(int n)
{
int i = 0;
int ret = 1;
for(i=1; i<=n; i++)
{
ret *= i;
}
return ret;
}
上述代码就是用迭代的方式解决阶乘问题,这样的方式并不比递归而且运算速度比递归要快上许多,所以,在解决问题的时候我们不能因为学习了递归就无脑使用递归的方法,我们要学会变通和熟练应用。