题外话:记录一下本人第一次学递归的感悟,现在还是处于一知半解的状态,后续会花时间去刷题巩固。这三道是y总讲的典型递归例题,很适合小白入门,采用画图+思路+代码的形式,希望能更好的帮助你理解,能对你学习递归有所帮助。
本文重点:思路+代码(思路字虽然写了很多,但是我感觉还是比较容易理解的,不要因为字多不去读=.=)
一、递归实现指数型枚举
题目:从1到n这n个整数中随机选取多个,输出所有可能的选择方案
图解示例:假如n=3
![](https://img-blog.csdnimg.cn/img_convert/44d83420bc8fb65b6696df69135785f2.png)
思路:n个数随机抽取输出,我们需要考虑每个数是会被输出还是不会,所以一共有n个位置,每个位置有三种状态,分为还没被枚举到,没有被选择(空),或者被选择。我们所选的循环体就可以是当前位置d,我们来找一下结束条件,当d>n时,即可输出答案。我们再来找一下一般情况,即当我们枚举这个位置时,我们需要进行哪些操作,从图中我们可以很明显的看出,我们需要给这个位置设置状态,例如,0表示还没枚举到,1表示空,2表示已被选择,最后不要忘记保护现场(将这个位置恢复成还没枚举到的状态)。
代码实现:
![](https://img-blog.csdnimg.cn/img_convert/fbeaca8562a5560900d8019dd98cfc84.png)
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 16;
int n;
int a[N];//记录每个位置状态,0代表未查找,1代表空,2代表使用
void dfs(int i) {
//结束位置
if (i > n) {
for (int j = 1; j <=n; j++) {//只有使用才会被输出
if (a[j] == 2) {
printf("%d", j);
}
}
puts("");
return;
}
//找中间位置
//两条分支,若为空
a[i] = 1;
dfs(i + 1);
//恢复现场
a[i] = 0;
//若使用
a[i] = 2;
dfs(i + 1);
//恢复现场
a[i] = 0;
}
int main() {
cin >> n;
dfs(1);//开始位置
}
二、递归实现排列型枚举
题目:把1-n这n个数排成一行随机打乱顺序,输出所有可能次序
图解示例:假如n=3
![](https://img-blog.csdnimg.cn/img_convert/53407d416c3d2412156531e44404c70e.png)
思路:一共有n个位置,我们将循环体设置为位置,枚举每个位置,结束条件,即为当前位置d>n,输出路径值。我们再来看一下一般情况,比如我们在枚举第二个位置的时候,我们需要知道当前位置能放哪些数,所以我们需要bool类型的数组来存储数是否被用过,没有用过的我们就可以进行选择,最后不要忘记恢复现场。
代码:
![](https://img-blog.csdnimg.cn/img_convert/1a0fc4b57f6e329f79718b082ffd691e.png)
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 16;
int n;
int a[N];//存储每个位置的数,0代表未选到
bool b[N];//判断每个数字是否被用过,bool型数组默认初值为false
void dfs(int d) {
//判断结束
if (d > n) {
//结束前输出结果
for (int i = 1; i <=n ; i++) {
printf("%d", a[i]);
}
puts("");
return;
}
//判断中间位置
for (int i = 1; i <= n; i++) {
if (!b[i]) {
a[d] = i;
b[i] = true;
dfs(d + 1);//向下递归
a[d] = 0;//恢复现场
b[i] = false;
}
}
}
int main() {
cin >> n;//几个位置几个数
dfs(1);//从第一个位置开始递归
return 0;
}
三、递归实现组合型枚举
题目:n个整数随机抽取m个,求所有排列(没有顺序即 1,2,3和2,1,3是一种组合),按字典序输出
图解示例:假如n=4,m=3
![](https://img-blog.csdnimg.cn/img_convert/49a199e4094b703a6713ea8af63a926b.png)
思路:抽取m个相等于有m个位置,那么我们可以选择枚举位置,结束条件即当前位置d>m。我们再来寻找一下一般情况,比如枚举到第二个位置,我们需要考虑当前位置可以选哪些数,由于是没有顺序的,而且是字典序,所以每个位置肯定要比前一个位置大,所以我们每次要给递归函数两个参数,即当前位置d和当前位置可以从几开始枚举到末尾,最后不要忘记恢复现场。
代码:
![](https://img-blog.csdnimg.cn/img_convert/acca4a3774ece25cab8238394da75c04.png)
#include<iostream>
using namespace std;
const int N = 26;
int m, n;
int a[N];//存储路径值
int start;//当前位置最小从几开始
void dfs(int d, int st) {
//结束
if (d > m) {
for (int i = 1; i <= m; i++) {
printf("%d", a[i]);
}
puts("");
return;
}
//中间位置
for(int i=start;i<=n;i++){
a[d] = i;
dfs(d + 1, i+ 1);
a[d] = 0;//恢复现场
}
}
int main() {
cin >> n >> m;
dfs(1,1);//第几个位置,可以从哪个数开始
return 0;
}