程序设计与算法(二):2. 递归

目录

例 汉诺塔

练习 面试题 08.06. 汉诺塔问题

例 N皇后

例 逆波兰数

例 表达式求值

例 爬楼梯

例 放苹果

练习 面试题 08.05. 递归乘法


例子源于慕课课程:程序设计与算法二

递归(recursion)思想用于

  1. 代替多重循环
  2. 有递归式子的问题
  3. 可以将问题分解成多个子问题求解

例 汉诺塔

问题描述略

分析

  • 分解成子问题,原位置src;目的地dest;中转位置mid
  • 1. 如果是只有一个盘子,直接从src移动到dest
  • 2. 如果多于一个盘子
  • 先将n-1个以dest为中转移到mid
  • 再将剩余的一个src移到dest
  • 最后将mid 上n-1个以src为中转移到dest

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<set>
using namespace std;
void Hanoi(int n, char src, char mid, char dest);

void Hanoi(int n, char src, char mid, char dest)
{
    if (n == 1) {
        cout << src << "->" << dest << endl;//如果只有一个,直接移到dest
        return;//递归中止
    }
    //否则分解任务
    Hanoi(n - 1, src, dest, mid);//如果不止一个,先将n-1个以dest为中转移到mid
    cout << src << "->" << dest << endl;//再将剩余的src移到dest
    Hanoi(n - 1, mid, src, dest);//最后将n-1个mid的,以src为中转移到dest
}

int main() {
    char src = 'A';
    char mid = 'B';
    char dest = 'C';
    Hanoi(2, src, mid, dest);

    return 0;
}

练习 面试题 08.06. 汉诺塔问题

参考代码

class Solution {
public:
    void hanota(vector<int>& A, vector<int>& B, vector<int>& C) {
        int n = A.size();
        move(n, A, B, C);
    }
    void move(int n, vector<int>& A, vector<int>& B, vector<int>& C) {
        if (n == 1) {
            C.push_back(A.back());
            A.pop_back();
            return;
        }
        else {
            move(n - 1, A, C, B);
            C.push_back(A.back());
            A.pop_back();
            move(n - 1, B, A, C);
        }
    }
};

例 N皇后

问题描述略,先了解一下八皇后,参考文章解释很清晰: 八皇后问题(递归回溯算法详解+C代码)_苍之羽-优快云博客_八皇后递归

八皇后代码

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<set>
using namespace std;
int notDanger(int row, int col); //判断是否冲突
void Print();                    //打印结果 “Q”表示皇后,“.”表示没有摆放皇后
void EightQueen(int row);        //row-1行已经摆好,从第row行开始摆

int  cnt = 0;                    //记录解法
int chess[8][8] = { 0 };         //0为没摆,1为摆放,注意我们只记录摆或没摆,而下一个会不会冲突通过列和对角线来判断,而不记录

int notDanger(int row, int col)  //判断该位置对应的列、左上和右上对角线是否已经摆放(因为该行左下右下不会摆放),如果摆放,则冲突,返回 0
{
	int i, j;
	//判断列
	for (i = 0; i < 8; i++) {
		if (chess[i][col] == 1)
			return 0;
	}
	//判断左上对角线
	for (i = row, j = col; i >= 0 && j >= 0; i--, j--) {
		if(chess[i][j]==1)
			return 0;
	}
	//判断右上对角线
	for (i = row, j = col; i >= 0 && j < 8; i--, j++) {
		if (chess[i][j] == 1)
			return 0;
	}
	return 1;
}

void Print()          //打印结果 
{
	int row, col;
	cout << "No:   " << cnt+1<<endl;
	for (row = 0; row < 8; row++) {
		for (col = 0; col < 8; col++) {
			if (chess[row][col] == 1)
			{
				cout << "Q";
			}
			else cout << ".";
		}
		cout << endl;
	}
	cout << endl;
}

void EightQueen(int row)             //row-1行已经摆好,从第row行开始摆
{
	if (row ==8 ) {
		Print();
		cnt++;
		return ;
	}

	for (int l = 0; l < 8; l++) {//从第l列开始尝试
		if (notDanger(row, l)) {//如果没有危险,摆放,进行下一行
			chess[row][l] = 1;
			EightQueen(row + 1);

			chess[row][l] = 0;   //如果row+1不能摆,说明row行位置需要调整,清零

		}
	}

}

int main()
{
	EightQueen(0);//从第0行开始摆放
	printf("总共有 %d 种解决方法!\n\n", cnt);
	return 0;
}

根据八皇后改写得N皇后代码

改动部分

  • main里读入皇后个数n
  • 二维数组大小更改为 chess[MAX][MAX](更好的方法为用vector)
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<set>
#define MAX 1000

using namespace std;
int notDanger(int row, int col); //判断是否冲突
void Print();                    //打印结果 “Q”表示皇后,“.”表示没有摆放皇后
void EightQueen(int row);        //row-1行已经摆好,从第row行开始摆
int n;
int  cnt = 0;                    //记录解法
int chess[MAX][MAX];
int notDanger(int row, int col)  //判断该位置对应的列、左上和右上对角线是否已经摆放(因为该行左下右下不会摆放),如果摆放,则冲突,返回 0
{
	int i, j;
	//判断列
	for (i = 0; i < n; i++) {
		if (chess[i][col] == 1)
			return 0;
	}
	//判断左上对角线
	for (i = row, j = col; i >= 0 && j >= 0; i--, j--) {
		if(chess[i][j]==1)
			return 0;
	}
	//判断右上对角线
	for (i = row, j = col; i >= 0 && j < n; i--, j++) {
		if (chess[i][j] == 1)
			return 0;
	}
	return 1;
}

void Print()          //打印结果 
{
	int row, col;
	cout << "No:   " << cnt+1<<endl;
	for (row = 0; row < n; row++) {
		for (col = 0; col < n; col++) {
			if (chess[row][col] == 1)
			{
				cout << "Q";
			}
			else cout << ".";
		}
		cout << endl;
	}
	cout << endl;
}

void EightQueen(int row)             //row-1行已经摆好,从第row行开始摆
{
	

	if (row ==n ) {
		Print();
		cnt++;
		return ;
	}

	for (int l = 0; l < n; l++) {//从第l列开始尝试
		if (notDanger(row, l)) {//如果没有危险,摆放,进行下一行
			chess[row][l] = 1;
			EightQueen(row + 1);

			chess[row][l] = 0;   //如果row+1不能摆,说明row行位置需要调整,清零

		}
	}

}

int main()
{
	cout << "请输入皇后个数" << endl;
	cin >> n;

	EightQueen(0);//从第0行开始摆放
	printf("总共有 %d 种解决方法!\n\n", cnt);
	return 0;
}

例 逆波兰数

 

输入:* + 11.0 12.0 + 24.0 35.0

输出:1357.000000

提示:上式为 (11.0+12.0)*(24.0+35.0);注意输入空格不要少!!

代码

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);
	//atof功能:解析字符串str,将其内容解释为浮点数,并将其值返回为double。
	//无法执行有效的转换,该函数将返回 0.0
	break;
	}
}

int main() {
	printf("%1f\n", exp());
	return 0;
}

注:atof的功能   atof()函数_没有西瓜汁-优快云博客_atof()

运行结果

 

例 表达式求值

 

解读下图:

1.表达式:可以由一项组成,也可以由多个项的加减运算组成

2.项:可以由一个因子组成,也可以由多个因子的乘除运算组成

3.因子:可以由(表达式)组成,也可以是一个整数(终止条件)

 

 代码

int expression_value(); //输入表达式,计算其值
int factor_value();		//输入因子,计算其值
int term_value();		//输入项,计算其值

//表达式由项组成
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 == '+')
				return result += value;//原来的结果加上下一项表达式
			else 
				return result -= value;
		}
		else more = false;
	}
	return result;
}
//项由因子组成
int term_value() {
	int result= factor_value();
	while (1) {
		char op = cin.peek();
		if (op == '*' || op == '/') {
			cin.get();
			int value= factor_value();
			if (op == '*')
				return result *= value;
			else	return 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';//数字字符c减去0的ascii码就是0-9的数
				cin.get();
				c=cin.peek();
			}
		}
	return result;
}

int main() {
	cout << expression_value() << endl;
	return 0;
}

例 爬楼梯

 

分析

  • 因为N可以很大,我们将问题分解成子规模来求解
  • 不管怎么走,总要迈出第一步,第一步可以走一级,也可以走两级,那么总的解法就是 第一次走一级时,后n-1步的解法+第一次走两级时,后n-2步的解法
  • 即: f(n)=f(n-1) + f(n-2)
  • 接下来是递归边界条件,判断在n满足什么条件时不需要递归,可知以下三种都是可以的

n<0  0

n=0  1(即不走)

n=0  1

n=1  1

n=1  1

n=2   2 

代码

#include<iostream>
using namespace std;

int stairs(int n) {
	if (n < 0)
		return 0;
	if (n == 1)
		return 1;
	else return stairs(n - 1) + stairs(n - 2);
}

int main() {
	int n;//台阶数
	while (cin >> n) {
		cout << stairs(n) << endl;
	}
}

例 放苹果

 

分析

  • 记f(i,k)为把i个苹果放进k个盘子里,则
  • 1. m<n,f(m,m) 
  • 2. m>=n,f(m,m) 苹果数大于等于盘子数,总方法=有空盘子时的放法+没空盘子的放法,即f(m,n)=f(m,n-1)+f(m-n,n)
  • 有空盘子时的放法:有空盘子时至少有一个是空的,这时剩余n-1个盘子,故为f(m,n-1)
  • 没空盘子的放法:没空的时,每个盘子至少放一个,剩余m-n个苹果,故为f(m-n,n)
  • 边界条件:剩余0个苹果时,只有一种放法,即不放;剩余0个盘子时,0种放法

代码

#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 N;
	cin >> N;
	while (N--) {
		int m, n;
		cin >> m >> n;
		cout << f(m, n)<<endl;
	}
}



输入输出:

 

练习 面试题 08.05. 递归乘法

分析

  • A*B可以解释为B个A
  • 用一个变量保存结果,逐个相加就行
  • 如果没有要求用递归,两个for循环可实现

class Solution {
public:
	int multiply(int A, int B) {
		int tmp = A;
		return re(tmp,A, B - 1);
	}

	int re(int tmp,int A, int B) {
		if (B == 0)
			return tmp;
		tmp += A;
		return re(tmp,A, B - 1);
	}
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

跳坑坑

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

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

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

打赏作者

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

抵扣说明:

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

余额充值