递归
一、定义
一个过程或函数时出现掉用本过程或本函数成分,成为递归(recursion)。
它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。直接调用自己称为直接递归,而将A调用B,B以调用A的递归叫做间接递归。
二、适用条件
必须满足以下两个条件:
1、必须有递归的终止条件;
2、过程描述中包含它本身。
三、递归格式
函数类型 函数名 (参数列表) {
if(边界条件满足) return 数值;
else return 函数名(参数列表);
}
四、经典题目
1、求n!的值
题目:
用递归算法,求n!的精确值(n以一般整数输入)。
分析:
此题符合递归的两个条件:
1、递归在计算到n时停止;
2、计算过程包含计算上个数的递归。
所以,此题写一个递归函数,当n==1时,传出1,否则传出(n-1)! * n。
代码:
#include <cstdio>
long long f(long long n) {
if (n == 0)
return 1;
else
return n * f(n - 1);
}
int main() {
long long n;
scanf("%lld", &n);
printf("%lld != %lld", n, f(n));
return 0;
}
2、汉诺塔问题
题目:
1、一次只许移动一个盘;
2、任何时候、任何柱子不允许把大盘放在小盘上面;
3、可使用任一一根立柱暂存圆盘;
问:如何使用最少步数实现n个盘子的移动?
分析:
此题符合递归的两个条件。
把三根柱子分别设为A, B, C。
首先, 先来分析两个盘子的情况:
1、只要把上面的1号从A移动到B;
2、把2号从A移动到C;
3、再把1号从B移动到C就可以了。
所以,对于n个圆盘可以分为三步:
1、将(n-1)个圆盘从A借助C移动到B;
2、将第n个圆盘从A移动到C;
3、将(n-1)个圆盘从B借助A移动到C。
代码:
#include <cstdio>
void f(int n, char a, char b, char c) {
if(n == 1) {
printf("%c->%c\n", a, c);
return ;
} else {
f(n - 1, a, c, b);
f(1, a, b, c);
f(n - 1, b, a, c);
}
}
int main() {
int n;
scanf("%d", &n);
f(n, 'A', 'B', 'C');
return 0;
}
四、优化方法
记忆化
递归是一种直观而有效的实现算法的方法。 但是,它会给性能带来一些不希望的损失,例如重复计算。 于是可以引入一种优化方法,记忆化(memoization)。
意思就是定义一个数组,来存储不同情况的答案,当遇到相同情况时,就不用再计算一次,直接返回之前的值。
经典题目
递归函数
题目:
对于一个递归函数w(a, b, c)。
如果a <= 0 or b <= 0 or c <= 0就返回值1;
如果a > 20 or b > 20 or c > 20就返回W(20,20,20);
如果a < b并且b < c 就返回w(a, b, c − 1) + w(a, b − 1, c − 1) − w(a, b − 1, c),
其它别的情况就返回w(a − 1, b, c) + w(a − 1, b − 1, c) + w(a − 1, b, c − 1) − w(a −1, b - 1, c - 1)。
分析:
此题主要在于用数组存储计算过的的答案,注意在判断时,应先判断第1与2个条件,再判断答案数组是否有值。
代码:
#include <cstdio>
long long t[300][300][300];
long long w(int a, int b, int c) {
if (a <= 0 || b <= 0 || c <= 0)
return 1;
else if (a > 20 || b > 20 || c > 20)
return t[20][20][20];
else if (t[a][b][c] != 0)
return t[a][b][c];
else if (a < b && b < c)
t[a][b][c] = w(a, b, c - 1) + w(a, b - 1, c - 1) - w(a, b - 1, c);
else
t[a][b][c] = w(a - 1, b, c) + w(a - 1, b - 1, c) + w(a - 1, b, c - 1) - w(a - 1, b - 1, c - 1);
return t[a][b][c];
}
int main() {
t[20][20][20] = w(20, 20, 20);
while (1) {
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
if (a == -1 && b == -1 && c == -1) {
return 0;
}
printf("w(%d,%d,%d)=%lld\n", a, b, c, w(a, b, c));
}
return 0;
}