算法入门 - 递归

 一个函数调用其自身就是递归

递归和普通函数调用一样都是利用栈来实现的 

例题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;
} 
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

漂流の少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值