一个函数调用其自身就是递归
递归和普通函数调用一样都是利用栈来实现的
例题1 求阶乘
#include <iostream>
using namespace std;
int Factorial(int n) {
if(n==0) {
return 1;
} else {
return n*Factorial(n-1);
}
}
int main() {
cout<<Factorial(5);
return 0;
}
例题2 汉诺塔问题
古代有一个梵塔,塔内有三个座A、B、C,A座上有64个盘子,盘子大小不等,大的在下,小的在上。有一个和尚想把64个盘子从A座移到C座,但是每次只能移动一个盘子,并且在移动过程中,3个座上的盘子始终保持在大盘在下,小盘在上。在移动过程中可以利用B座,要求输出移动的步骤。
#include <iostream>
using namespace std;
void Hanoi(int n,char src,char mid,char dest) {
// 将src座上的n个盘子,以mid座为中转,移动dest座
if(n==1){
cout << src << "->" << dest <<endl;
return ;
}
// 先将n-1个盘子从src移到mid
Hanoi(n-1,src,dest,mid);
// 再将一个盘子从src移到dest
cout << src << "->" << dest << endl;
// 最后将n-1个盘子从mid移动到dest
Hanoi(n-1,mid,src,dest);
return ;
}
int main() {
int n;
cin >> n;
Hanoi(n,'A','B','C');
return 0;
}
例题3 n皇后问题
用递归解决多重循环
输入一个正整数N,则程序输出N皇后问题的全部摆法。
输出结果的每一行都代表一种摆法。每一行,第i个数字如果是j,就代表第i行的皇后应该放在第n列。皇后的行列编号都是从1开始算。
样例输入:4
样例输出:2 4 1 3
3 1 4 2
#include <iostream>
#include <cmath>
using namespace std;
int N;
int queenPos[100];
void NQueen(int k);
int main() {
cin >> N;
NQueen(0); //从第0行开始
return 0;
}
// 在0~k-1行皇后已经摆好的情况下,摆第k行的皇后
void NQueen(int k) {
int i;
if(k==N){
for(i=0;i<N;++i) {
cout << queenPos[i] + 1 << " ";
}
cout << endl;
return;
}
// 逐个尝试第k个皇后的位置
for(i=0;i<N;++i) {
int j;
for(j=0;j<k;++j) {
// 和已经摆好的k个皇后的位置比较,看是否冲突
if(queenPos[j] == i || abs(k-j) == abs(i - queenPos[j])) {
break; // 冲突,测下一个位置
}
}
// 当前选的位置i不冲突
if(j == k) {
queenPos[k] = i;
NQueen(k+1);
}
}
}
例题4 波兰表达式
用递归解决递归形式的问题
波兰表达式是一种把运算符前置的算术表达式。
例如普通的表达式2+3的逆波兰表示法为+ 2 3。
波兰表达式的优点是运算符之间不必有优先级关系, 也不必用括号改变运算次序。
例如(2+3)4的逆波兰表达法为 + 2 3 4。
本题求解逆波兰表达式的值, 其中运算符包括+、-、*、/四个.
输入格式: 1行, 其中运算符和运算数之间都用空格分隔, 运算数是浮点数。
输出格式: 1行, 表达式的值。
#include <iostream>
#include <cstdlib>
using namespace std;
double exp() {
char s[20];
cin >> s;
switch(s[0]) {
case '+': return exp() + exp();
case '-': return exp() - exp();
case '*': return exp() * exp();
case '/': return exp() / exp();
default: return atof(s);
}
}
int main() {
printf("%lf",exp());
return 0;
}
例题5 表达式计算
用递归解决递归形式的问题
输入为四则运算表达式,仅由整数、+、-、*、/、(、) 组成,没有空格,要求求其值。假设运算符结果都是整数。“/”结果也是整数。
表达式是一个递归的定义
表达式可以看做是单项式的加减
单项式可以看做是因子的乘除
因子可以是整数或者表达式
#include <iostream>
#include <cstdlib>
using namespace std;
int factor_value();
int term_value();
int expression_value();
int main() {
cout << expression_value() << endl;
return 0;
}
// 求一个表达式的值
int expression_value() {
int result = term_value(); //求第一项的值
bool more = true;
while(more) {
char op = cin.peek(); //看一个字符,不取走
if(op == '+' || op == '-') {
cin.get(); //从输入中取走一个字符
int value = term_value();
if(op == '+') {
result += value;
} else {
result -= value;
}
} else {
more = false;
}
}
return result;
}
// 求一个项的值
int term_value() {
int result = factor_value(); // 求第一个因子的值
while(true) {
char op = cin.peek();
if(op == '*' || op == '/') {
cin.get();
int value = factor_value();
if(op == '*') {
result *= value;
} else {
result /= value;
}
} else {
break;
}
}
return result;
}
// 求一个因子的值
int factor_value() {
int result = 0;
char c = cin.peek();
if(c == '(') {
cin.get();
result = expression_value();
cin.get();
} else {
while(isdigit(c)) {
result = 10 * result + c - '0';
cin.get();
c = cin.peek();
}
}
return result;
}
例题6 爬楼梯
用递归将问题分解为规模更小的子问题进行求解
小明爬楼梯,他可以每次走1级或2级,输入楼梯的级数,求不同的走法数。
输入:有若干行,每行包含一个正整数N,代表楼梯级数,1<= N <= 30
输出:不同的走法数,每一行输入对应一行输出
分析
n级台阶的走法 = 先走一级后,n-1级台阶的走法 + 先走两级后,n-2级台阶的走法
f(n) = f(n-1) + f(n-2)
#include <iostream>
using namespace std;
int N;
int stairs(int n) {
if(n < 0) return 0;
if(n == 0) return 1;
return stairs(n-1) + stairs(n-2);
}
int main() {
while(cin >> N) {
cout << stairs(N) << endl;
}
return 0;
}
例题7 放苹果
把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?5,1,1和1,5,1是同一种分法。
输入
第一行是测试数据的数目t(0<=t<=20)。以下每行均包含两个整数M和N,以空格分开。M>=1,N<=10。
输出
用一行输出分法的数量。
分析
设i个苹果放在k个盘子里的放法总数是f(i,k),则
k>i,f(i,k)=f(i,i)
k<=i,总放法=有盘子为空的放法+没盘子为空的放法
f(i,k) = f(i,k-1)+f(i-k,k)
然后,再确定一下边界条件。
#include <iostream>
using namespace std;
int f(int m, int n) {
if(n>m) {
return f(m,m);
}
if( m == 0) {
return 1;
}
if (n <= 0) {
return 0;
}
return f(m,n-1) + f(m-n,n);
}
int main() {
int t,m,n;
cin >> t;
while(t--) {
cin >> m >> n;
cout << f(m,n) << endl;
}
return 0;
}
例题8 算24
给出4个小于10的正整数,你可以使用加减乘除4种运算以及括号把这4个数连接起来得到一个表达式。看看能不能找到一个表达式的结果为24。
输入
输入数据包括多行,每行给出一组测试数据,最后一组测试数据中包括4个0,表示输入的结束。
输出
对于每一组测试数据,输出一行,如果可以得到24,输出“YES”,否则,输出“NO”
样例输入
5 5 5 1
1 1 4 2
0 0 0 0
样例输出
YES
NO
分析
n个数算24,必有两个数要先算。这两个数算的结果,和剩余n-2个数,就构成了n-1个数求24的问题。
枚举先算的两个数,以及这两个数的运算方式。
边界条件:用一个数算24
#include <iostream>
#include <cmath>
using namespace std;
double a[5];
#define EPS 1e-6
bool isZero(double x) {
return fabs(x) <= EPS;
}
// 用数组a中的n个数计算24
bool count24(double a[],int n) {
double b[5];
if( n == 1) {
if(isZero(a[0]-24))
return true;
else
return false;
}
// 枚举两个数的组合
for(int i = 0;i < n-1; i++) {
for(int j = i+1;j < n; j++) {
int m = 0;
// 把其余数放入b
for(int k = 0;k < n;++k) {
if(k!=i&&k!=j)
b[m++] = a[k];
}
b[m] = a[i] + a[j];
if(count24(b,m+1)) return true;
b[m] = a[i] - a[j];
if(count24(b,m+1)) return true;
b[m] = a[j] - a[i];
if(count24(b,m+1)) return true;
b[m] = a[i] * a[j];
if(count24(b,m+1)) return true;
if(!isZero(a[j])) {
b[m] = a[i] / a[j];
if(count24(b,m+1)) return true;
}
if(!isZero(a[i])) {
b[m] = a[j] / a[i];
if(count24(b,m+1)) return true;
}
}
}
return false;
}
int main() {
while(true) {
for(int i=0;i<4;i++)
cin >> a[i];
if(isZero(a[0]))
break;
if(count24(a,4))
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}