题目
把 1∼n这 n 个整数排成一行后随机打乱顺序,输出所有可能的次序。
输入格式
一个整数 n。
输出格式
按照从小到大的顺序输出所有方案,每行 1 个。
首先,同一行相邻两个数用一个空格隔开。
其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面。
数据范围
1≤n≤9
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
具体思路如下:
-
全局变量定义:
path
数组用于存储排列的数字,vis
数组用于记录每个数字是否已经被使用过 -
深度优先搜索:
-
如果
u
大于n
,表示已经填满了整个排列,那么就输出这个排列,然后返回。 -
否则,对于
1
到n
的每一个数字i
,如果i
还没有被使用过,那么就将i
标记为已经被使用,然后将i
放入排列的第u
位,然后递归地调用DFS(u+1)
,表示继续填充下一位。最后,取消i
的标记,表示回溯。
-
核心思想是深度优先搜索和回溯。在每一步中,都尝试将每一个还没有被使用过的数字放入当前的位置,然后递归地填充下一位。如果已经填满了整个排列,那么就输出这个排列。否则,如果当前的数字不能放入当前的位置,那么就回溯,尝试下一个数字。这样,就可以保证生成的是所有可能的排列,而不是某一部分的排列。这是一个非常经典的深度优先搜索和回溯的应用。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=10; // 定义全局常量N,表示排列的长度
int path[N]; // 用于存储排列的数组
int vis[N]; // 记录每个数字是否已经被使用过
int n; // 输入的排列长度
// 深度优先搜索函数
void DFS(int u)
{
if(u>n) // 如果已经填满了整个排列
{
for(int i=1;i<=n;i++)
cout<<path[i]<<" "; // 输出排列
cout<<endl;
return ;
}
for(int i=1;i<=n;i++)
{
if(!vis[i]) // 如果数字i还没有被使用过
{
vis[i]=1; // 标记数字i已经被使用
path[u]=i; // 将数字i放入排列的第u位
DFS(u+1); // 继续填充下一位
vis[i]=0; // 回溯,取消数字i的标记
}
}
}
int main()
{
cin>>n; // 输入排列的长度
DFS(1); // 从第一位开始填充排列
return 0;
}
关键点来了,许多小伙伴不知道回溯的流程,我刚开始也卡了好久,下面我用代码手动模拟了一下回溯流程,相信你们一定会有所收获。
DFS(1)
{
for(int i=1;i<=n;i++)
{
if(!vis[1])
{
vis[1]=1;
path[1]=1;
DFS(2)
{
for(int i=1;i<=n;i++)
{
if(!vis[2])
{
vis[2]=1;
path[2]=2;
DFS(3)
{
for(int i=1;i<=n;i++)
{
if(!vis[3])
{
vis[3]=1;
path[3]=3;
DFS(4)
{
cout<<1 2 3 ; //输出1 2 3
return ;
}
vis[3]=0;
//此时DFS(3)结束,回溯到DFS(2)
}
}
}
vis[2]=0;
//i++,此时i=3
if(!vis[3]=0)
{
vis[3]=0;
path[2]=3;
DFS(3)
{
for(int i=1;i<=n;i++)
{
if(!vis[2]=0)
{
vis[2]=1;
path[2]=3;
DFS(4)
{
cout<< 1 3 2; //输出 1 3 2
}
...........
}
}
}
}
}
}
}
}
}
}