1.递归是什么
函数自己调用自己。
2.使用递归的限制
2个限制条件:
每次递归调⽤之后越来越接近这个限制条件,当满足限制条件时,递归便停止。
看栗子:求n的阶乘,n! = n ∗ (n − 1)!
2个限制条件如下:
代码实现
不考虑n太大,因为会溢出(后面结尾解释为什么溢出,并且求阶乘的更好的方法是迭代不是递归)
#include <stdio.h>
int Ret(int n)
{
if (n <= 0)
return 1;
else
return Ret(n - 1) * n;
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = Ret(n);
printf("%d的阶乘是%d", n, ret);
return 0;
}
运行结果:
为了更容易观察代码的变化,动图里面,我将代码里面的Ret(n-1)*n的结果视觉化成num,把返回值回来时的n用 n=n 视觉化成n,这样子,可以用监视窗口观察n和num的变化。
-
如果满足第一个限制条件,代码是怎么运行的,输入0进行调试。
(如果不懂vs怎么调试的,可以移步我的这篇博客 http://t.csdn.cn/h8roc)
我是直接F11进行调试,随着箭头的移动,代码在一步一步进行。 -
如果满足第二个限制条件,代码是怎么运行的,输入0进行调试。
为了更容易观察代码的变化,把Ret(n-1)*n的结果视觉化成num,把返回值回来时的n用 n=n 视觉化成n,这样子,可以用监视窗口观察n和num的变化。
动图描述:
文字描述:
看到 n 输入1的时候,跳到 Ret(n-1)*n 那里时,函数开始调用自己,不断n-1,我们也可以看到n不断变化:1,0
然后再不断返回自己的值,比如调用到n=0时,return 1,然后跳到n=1时,num=(返回的1)乘(n=1),num=1,再return num,因为n=1是最初的n,所以num是直接返回主函数的Ret(n)。
画图描述:
这是输入2的代码递推和回归过程:
3.讨论n太大为什么会栈溢出?
每⼀次函数调⽤,都需要为本次函数调⽤在栈区申请⼀块内存空间,保存函数调⽤期间的局部变量的值,
这块空间被称为运⾏时堆栈,或者函数栈帧。
假设我们的n是100
函数递归一直不返回,函数对应的栈帧空间就⼀直占⽤,直到函数递归不再继续,开始回归,才逐层释放栈帧空间,所以如果采⽤函数递归的⽅式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢出(stack over flow)的问题。
所以如果不想使⽤递归,通常我们用迭代(一般是循环的⽅式)
int main()
{
int i = 0;
int n = 1;
scanf("%d",&n);
int ret = 1;
for (i = 1; i <= n; i++)
{
ret *= i;
}
return 0;
}
此时该问题的求解,迭代实现往往⽐递归实现效率更⾼。
所以我们要看问题选择解题方法,当⼀个问题⾮常复杂,难以使⽤迭代的⽅式实现时,此时递归实现的简洁性便可以补偿它所带来的运⾏时开销。
//下一章我们更新用递归解决的例子。
1.顺序打印⼀个整数的每⼀位
2.求第n个斐波那契数
3.编写一个函数实现n的k次方,使用递归实现
4.写一个递归函数DigitSum(n),输入一个非负整数,返回组成它的数字之和