浅浅的谈一下递归(蓝桥杯)

从蓝桥杯的角度看看递归

依托具体题目,AC代码及其题解,谈谈我对递归的理解

题目1

从 1∼n1∼n 这 nn 个整数中随机选取任意多个,输出所有可能的选择方案。

输入格式

输入一个整数 nn。

输出格式

每行输出一种方案。

同一行内的数必须升序排列,相邻两个数用恰好 11 个空格隔开。

对于没有选任何数的方案,输出空行。

本题有自定义校验器(SPJ),各行(不同方案)之间的顺序任意。

数据范围

1≤n≤151≤n≤15

输入样例:
3
输出样例:
3
2
2 3
1
1 3
1 2
1 2 3

题解1

AC代码

#include <bits/stdc++.h>
using namespace std;
int n;
const int N = 16;
int st[N];

void dfs(int u) {
	if (u > n) {
		for (int i = 1; i <= n; ++i) {
			if (st[i] == 1) {
				cout << i << ' ';
			}
		}
		cout << endl;
		return ;
	}
	st[u] = 1;
	dfs(u + 1);
	st[u] = 0;
	st[u] = 2;
	dfs(u + 1);
	st[u] = 0;
}
int main() {
	cin >> n;
	dfs(1);
	return 0;
}

问题

  1. 数据范围是(1 <= 15)在设定上限的时候为什么设定为 16
  2. 搞不懂递归
  3. 为什么在 18行 后面要把 st[u] 还原

解释

  1. 因为里面的有效数据是从 1 开始的,你也可以尝试着从 0 开始

  2. 递归的定义中,自己调用自己,并不准确,自己调用自己一定是死递归,而我们的递归之所以会自动停下,是因为,每次递归调用的时候,函数本身已经向我们的理想状态偏移了。

    另外,看不懂递归大约是因为不肯动手去画递归树。

    当你看不懂一个程序的时候,你最好拿最简单的数据,严格按照电脑的执行方式去执行一次(我们现在也可以运用调试功能,但是我还是觉得手动更好)

  3. 能执行 19 行代表 18 行语句执行完毕,所以要把数据恢复,便于下一次的调用

    在这里面:

    st[i] == 0 代表 i 数据暂未考虑

    st[i] == 1 代表 i 数据选

    st[i] == 2 代表 i 数据不选

可视化的展示

假设此时输入的 n 为 3

u =1u>n ? ×st[1] = 1dfs(1+1)
u = 2u>n ? ×st[2] = 1dfs(2+1)
u = 3u>n ? ×st[3] = 1dfs(3+1)
u = 4u>n? √ ==> 循环输出已有的结果st[3] = 0 && st[3] = 2dfs(3+1)
u = 4u>n? √ ==> 循环输出已有的结果st[3] = 0return;返回上一层

其实这个,是谁写谁知道!手工写一下,比苦思半天更有效果!

题目2

把 1∼n1∼n 这 nn 个整数排成一行后随机打乱顺序,输出所有可能的次序。

输入格式

一个整数 nn。

输出格式

按照从小到大的顺序输出所有方案,每行 11 个。

首先,同一行相邻两个数用一个空格隔开。

其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面。

数据范围

1≤n≤91≤n≤9

输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

题解2

AC代码

#include <bits/stdc++.h>
using namespace std;
int n;
const int N = 10;
int st[N];
bool used[N];

void dfs(int u) {
	if (u > n) {
		for (int i = 1; i <= n; ++i) {
			cout << st[i] << ' ';
		}
		cout << endl;
		return ;
	}
	for (int i = 1; i <= n; ++i) {
		if (!used[i]) {
			used[i] = true;
			st[u] = i;
			dfs(u + 1);
			st[u] = 0;
			used[i] = false;
		}
	}
}

int main() {
	cin >> n;
	dfs(1);
	return 0;
}

问题

  1. st[] 和 used[] 分别代表什么
  2. 这道题和上面那道题有什么区别吗?
  3. 递归搞不懂

解释

  1. st[] 数组表示目前排列的数据,used[] 表示该数字是否用

  2. 大方向上都是深度优先搜索,并没有什么区别,细分的化就是代码实现上不同了。

  3. 亲,自己动手模拟一下比我讲半天都有用。

    还是那句话,谁亲自动手写了谁就一定会

可视化展示

还是以 n = 3 进行演示

u = 1u > n ? ×used[1] = false ? √used[1] = truest[1] = 1dfs(1+1)
u = 2u > n ? ×used[1] = false ? × ==> used[2] == false ? √used[2] = truestr[2] = 2dfs(2+1)
u = 3u > n ?×used[1] ……used[3] == false? √used[3] = truestr[3] = 3dfs(3+1)
u = 4u > n ? √对结果进行输出st[3] = 0usde[3] = false……

我承认这个展示确实不算太完整,但是相信用心的人还是能看懂

另附上代码的运行结果

1 2 3 
1 3 2 
2 1 3 
2 3 1 
3 1 2 
3 2 1 

小结

  1. 在写递归函数的时候,我们总是爱把递归的出口放在函数的头部
  2. 递归代码的缺点是难以理解,优点是简短
  3. 套用上面两个dfs(深度优先)的递归算法,可以帮助我们更丝滑的刷题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值