一.什么是递归?
程序调用自身的编程技巧称为递归( recursion)。
递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接 调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解, 递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
递归的主要思考方式在于:把大事化小
二.递归的两个必要条件
1.存在限制条件,当满足这个限制条件的时候,递归便不再继续。
2.每次递归调用之后越来越接近这个限制条件。
三.递归的应用
首先先举一个简单的例子
1.n的阶乘
n的阶乘,是将从1到n之间的整数相乘。
按照迭代的方式,我们可以通过循环来完成这个工作
int i;
int n;
int fac=1;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
fac=fac*i;
}
而我们也可以按照递归的方式来完成
首先我们需要创建一个函数Fac(n)
根据递归的概念,我们可以初步确定这个函数

而根据递归的思想,我们可以将函数看作以下方式
同理,Fac(n-1)=(n-1)*Fac(n-2)
而当n=1时,阶乘应该停止,所以我们将限制条件设置为n>1。
以此类推,每次函数调用都会接近n>1这个限制条件,我们便可以以递归的方式完成n的阶乘
int Fac(int n)
{
if(n>1)
return n*Fac(n-1);
else
return 1;
}
int main()
{
int n;
scanf("%d",&n);
int fac=Fac(n);
return 0;
}
2.顺序依次打印整形值
例如一个整形值num=1234
想要顺序打印,我们首先需要获取这个整形值每一位的数字
我们首先想到的便是利用取商和取余
num/10=123
num%10=4
而函数结束的条件应为print()的参数num=0,由此我们可以确立这个函数
void Print(int num)
{
if(num>0)
{
Print(num/10);
printf("%d ",num%10);
}
else
printf("\n");
}
int main()
{
int num;
scanf("%d",&num);
Print(num);
}
四.递归与迭代
1.递归出现的问题
(1).运行时间长
首先我们先看一下递归方法下的斐波那契数列
int fib(int n)
{
if (n <= 2)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
当n的值较小时,我们可以轻易的求出第n个斐波那契数
而n的值较大时,计算的时间便被大大延长
那么这是为什么呢?

我们可以看到,在递归形式下的菲波那切数列,随着递归的进行,会出现重复的调用。
而随着n的增大,递归次数增多,重复调用的问题也会更加严重,严重影响程序运行时间。
我们可以将代码作出以下修改
int count = 0;
int fib(int n)
{
if(n == 3)
count++;
if (n <= 2)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
这样,我们便可以根据count的值来判断fib(3)所调用的次数
当n等于20时,fib(3)就已经被调用了2584次,严重影响程序运行时间。
(2).栈溢出的报错
在上面所举的n的阶乘的例子中
int Fac(int n)
{
if(n>1)
return n*Fac(n-1);
else
return 1;
}
int main()
{
int n;
scanf("%d",&n);
int fac=Fac(n);
return 0;
}
若让n=10000,程序会发生崩溃,会报错: stack overflow(栈溢出)
这有是为什么呢?
这是由于每一次调用函数,都会为本次函数在内存的栈区上开辟一块内存空间,系统分配给程序的栈空间是有限的,因此递归层数过深会导致栈空间耗尽,这种现象称为栈溢出。
2.解决方法
(1). 将递归改写成非递归。
(2). 使用static对象替代 nonstatic 局部对象。 在递归函数设计中,可以使用 static 对象替代 nonstatic 局部对象(即栈对象),这不仅可以减少每次递归调用和返回时产生和释放 nonstatic 对象的开销,而且 static 对象还可以保存递归调用的中间状态,并且可为各个调用层所访问。
3.其他
(1). 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。
(2). 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。
总结
本篇文章只是对函数递归进行了最初步的讲解,而如果想要熟练使用函数递归解决问题,需要我们进行大量的思考练习。
这里提供两个函数递归的经典问题供大家思考:
1. 汉诺塔问题
2. 青蛙跳台阶问题
3001

被折叠的 条评论
为什么被折叠?



