递归算法是一种程序设计技巧,在程序设计语言中广泛应用。首先评价一下该算法:
优点:
- 结构清晰,可读性强,而且容易用数学归纳法来证明算法的正确性,因此它为设计算法、调试程序带来很大方便。
- 简洁,递归的实现明显要比循环简单得多。
缺点:
- 递归由于是函数调用自身,而函数调用是有时间和空间的消耗的:每一次函数调用,都需要在内存栈中分配空间以保存参数、返回地址以及临时变量,而往栈中压入数据和弹出数据都需要时间,导致效率较低。
- 递归中很多计算都是重复的,由于其本质是把一个问题分解成两个或者多个小问题,多个小问题存在相互重叠的部分,则存在重复计算,如fibonacci斐波那契数列的递归实现。这也导致了效率较低。
- 调用栈可能会溢出,每一次函数调用会在内存栈中分配空间,而每个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。这也影响了性能。
例一:汉诺塔问题。
输出汉诺塔问题的全部移动步骤,初始状态所有圆盘在B塔,要求移动到A塔。
#include <stdio.h>
void hanoi(int n, char a, char b, char c) {
if (n == 1) {
printf("%c -> %c\n", a, c);
} else {
hanoi(n-1, a, c, b);
printf("%c -> %c\n", a, c);
hanoi(n-1, b, a, c);
}
}
int main() {
int n = 3; // 盘子的数量
char a = 'B', b = 'A', c = 'C';
hanoi(n, a, b, c);
return 0;
}
在上述代码中,hanoi函数的参数分别表示需要移动的盘子数目、起始柱子、中间柱子和目标柱子。当只有一个盘子需要移动时,直接将该盘子从起始柱子移动到目标柱子即可。当需要移动n个盘子时,可以先将前n-1个盘子从起始柱子移动到中间柱子,将第n个盘子从起始柱子移动到目标柱子,最后将前n-1个盘子从中间柱子移动到目标柱子。
例二:正整数逆序输出
输入任意一个不大于9位的正整数,输出各位数的逆序形式,例如:输入12345,输出54321。下面将分别利用递归和递推两种方式求解。
#include <stdio.h> //递归
void reverse(int n) {
if (n > 0) {
printf("%d", n % 10);
reverse(n / 10);
}
}
int main() {
int n;
printf("请输入一个不多于9位的正整数:");
scanf("%d", &n);
printf("逆序数为:");
reverse(n);
printf("\n");
return 0;
}
#include <stdio.h> // 递推
int main() {
int num, s = 0;
printf("请输入一个不多于9位的正整数:");
scanf("%d", &num);
while (num) {
s = s * 10 + num % 10;
num /= 10;
}
printf("逆序数为:%d\n", s);
return 0;
}
递推算法是一种用若干步可重复运算来描述复杂问题的方法,通常是通过计算机前面的一些项来得出序列中的指定象的值。递推相对于递归算法,免除了数据进出栈的过程,也就是说,不需要函数不断的向边界值靠拢,而直接从边界出发,直到求出函数值。递推的优点包括效率高,不易出现栈溢出等问题,可以优化空间复杂度。递推的缺点是实现起来比较困难,需要找到递推公式,而且递推公式可能不是显然的。