递归
定义: 若一个对象部分地包含自己,或者它自己给自己定义,则这个对象是递归的。
若一个过程直接地或间接地调用自己,则这个过程为递归过程。
以下三种情况会用到递归:
-
递归定义的数学函数
-
具有递归特性的数据结构
-
可递归求解的问题
分治法求解递归问题算法的一般形式:
void p(参数表)
{
if (递归结束条件)
可直接求解步骤; //基本项
else
p(较小的参数); //归纳项
}
例如:
long Fact(long n)
{
if (n == 0)
return 1; //基本项
else
return n*Fact(n - 1); //归纳项
}
函数调用过程
- 调用前,系统完成:
- 将实参,返回地址等传递给被调用函数
- 为被调用函数的局部变量分配存储区
- 将控制转移到被调用函数的入口
- 调用后,系统完成:
- 保存被调用函数的计算结果
- 释放被调用函数的数据区
- 依照被调用函数保存的返回地址将控制转移到调用函数
当多个函数构成嵌套调用时,此时这些函数遵循后调用的先返回的规则。
上图中,函数调用的顺序是: m a i n → f a c t → m y p o w main \to fact \to mypow main→fact→mypow,而参数返回的顺序是: m y p o w → f a c t → m a i n mypow \to fact \to main mypow→fact→main。所以,若果把一个函数看作是线性表中的元素,那么由多个函数组成的线性表就可以看作是一个栈。
栈与递归
递归过程本身就是一个多函数调用(自己多次调用自己),所以它本身就是一个栈。
例如,求解4的阶乘。
递归函数调用的实现:
“递归工作栈”:递归程序运行期间使用的数据存储区。
“工作记录”:存有实参,局部变量,返回地址。
递归的优缺点:
优点:结构清晰,程序易读。
缺点:每次调用都要生成工作记录,保存状态信息,入栈;返回时都要出栈,回复状态信息。时间开销大。
为了避免递归的缺点,可以把递归实现的算法变成非递归实现,通常由两种方法:
- 尾递归、单项递归:可以用循环来实现
- 自用栈模拟系统运行时的栈(不常用)