0003 || 递归实现排列型枚举 (DFS深度优先搜索)
题目
把 1 ∼ n 这 n 个整数排成一行后随机打乱顺序,输出所有可能的次序。
输入格式
一个整数 n。
输出格式
按照从小到大的顺序输出所有方案,每行 11 个。
首先,同一行相邻两个数用一个空格隔开。
其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面。
数据范围
1 ≤ n ≤ 9
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
以下是代码的实现
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10; // 起点是 1, 那么终点是9的话,数组需要加一位。
int st[N]; // 声明的全局变量在C++中,默认全部赋值为0.
int n;
bool use[N]; // 用来声明当前数的状态是用过还是没用过,用过true, 没用过false。
void dfs(int u)
{
if(u > n) //用来判断枚举完了最后一位,用来最后的输出。
{
for(int i = 1; i <= n; i++)
{
printf("%d ", st[i]); //st[i]用来存放当前值。
}
printf("\n");
return;
}
for(int i = 1; i <= n; i++)
{
if(!use[i]) //用来判断当前位数是否存在值,不存在数,
{ //进行枚举之后接着进入下一个递归,进行枚枚举。
st[u] = i;
use[i] = true;
dfs(u + 1);
st[u] = 0; //恢复现场
use[i] = false;
}
}
}
int main()
{
scanf("%d", &n);
dfs(1);
return 0;
}
思想总结
对于这个排列型枚举, 在我看来是先实现对于递归树的实现。对于这个题目中没个位数出现的可能性用树列出来。
其次,对树进行代码实现,可以利用递归的方法从最后一层进行实现, 即输出。
我们还需要对每一层的状态进行判断,所以我们用了 use[N] 这样的数组来进行实现, 我们对于每个位数的使用状态进行判断从而赋值再递归。
最后, 我们需要对赋值后递归下一层返回当层进行赋值,即恢复现场,我们不能对赋值递归后的当前层数进行破坏后不回复,这样子就会进行死循环。
特别的,在主函数外进行定义的时候,我们的定义并不需要初始化,因为已经直接赋值为 0 了。是因为局部变量是在栈下执行的,而栈是用来执行指令的。全局变量是在堆上执行的,是用来存数据的,标记为 0 了。
我认为在DFS中很有意思的事情就是,我们很需要一个数组来记录每一层递归下的状态,随后再递归到下一层,之后我们需要对当前层恢复现场,就像原本没发生赋值一样,而在记录当前层状态时,同时的,我们需要对当前层进行操作赋值(即对当前位数进行操作),使得他在下一层递归不会因为上一层没操作(即上一位)而导致我们下一层无限循环最后time limit。以上就是我认为DFS深度优化搜索的特点。