递归的概念
若一个算法直接或间接地调用自己本身,则称这个算法是递归算法。递归算法把问题分解为形式更加简单的子问题的方法来求解问题。
递归三原则:
- 递归算法必须有基本情况
- 递归算法必须改变其状态并向基本情况靠近
- 递归算法必须递归的调用自己
递归经典案例:汉诺塔
用 Python 解决汉诺塔问题:
def moveDisk(fp, tp):
print("moving disk from", fp, "to", tp)
def moveTower(height, fromPole, toPole, withPole):
if height >= 1:
moveTower(height-1, fromPole, withPole, toPole)
moveDisk(fromPole, toPole)
moveTower(height-1, withPole, toPole, fromPole)
moveTower(3, 'a', 'c', 'b')
执行情况:
moving disk from a to c
moving disk from a to b
moving disk from c to b
moving disk from a to c
moving disk from b to a
moving disk from b to c
moving disk from a to c
用 C 解决汉诺塔问题:
#include <stdio.h>
void towers(int n, char fromPeg, char toPeg, char auxPeg)
{
if (1 == n)
{
printf("%s%c%s%c\n", "move disk 1 from peg ", fromPeg, " to peg ", toPeg);
return;
}
towers(n-1, fromPeg, auxPeg, toPeg);
printf("%s%d%s%c%s%c\n", "move disk ", n, " from peg ", fromPeg, " to peg ", toPeg);
towers(n-1, fromPeg, toPeg, auxPeg);
}
int main(int argc, char *argv[])
{
towers(3, 'A', 'C', 'B');
return 0;
}
执行情况:
move disk 1 from peg A to peg C
move disk 2 from peg A to peg B
move disk 1 from peg A to peg B
move disk 3 from peg A to peg C
move disk 1 from peg A to peg B
move disk 2 from peg A to peg C
move disk 1 from peg A to peg C
递归过程
递归算法的执行过程是不断地自调用过程,直到到达递归出口才结束自调用;到达递归出口后,递归算法开始按”最后调用地过程最先返回地顺序“返回;返回到最外层地调用语句时,递归算法执行过程结束。
对于非递归函数,调用函数在调用被调用函数之前,系统要保存调用函数的返回地址和调用函数的局部变量值。当执行完被调用的函数,返回调用函数前,系统首先恢复调用函数的局部变量值,然后返回调用函数的返回地址。
递归函数和非递归函数的调用形式基本一致,但保存信息的方法不同。递归函数别调用时,系统在运行递归函数前也要保存调用函数的返回地址和调用函数的局部变量值,而且因为递归函数反复调用同一个函数,并且要求后被调用的函数先返回,所以高级程序语言利用堆栈(利用堆栈后入先出的特性)来保存递归函数调用时的信息。系统用于保存递归函数调用信息的堆栈称为运行时堆栈。