给定一个整数 n,将数字 1∼n 排成一排,将会有很多种排列方法。
现在,请你按照字典序将所有的排列方法输出。
输入格式
共一行,包含一个整数 n。
输出格式
按字典序输出所有排列方案,每个方案占一行。
数据范围
1≤n≤7
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
#include
using namespace std;
int n;
const int N = 10;
int path[N];//存放dfs时,路径上的值
bool st[N];//记录当前这个位置的状态,用于判断是否被用过
void dfs(int u)
{
//u已经递归到最终的n这层,表示已经对前n个位置部署好了相应的数字,属于搜索完成的情况,此时可以输出这条路径中存的值
if(u == n)
{
for(int i = 0; i < n;i++) cout << path[i]<<' ';
cout << endl;
return;
}
for(int i = 1; i <= n;i++)
{
//判断当前这个值有没有用过
if(!st[i])
{
path[u] = i;//把这个数存到路径数组中
st[i] = true;//标记这个数已经用过了
dfs(u+1);//第u个位置的数据已经部署好了,现在需要走到下一个位置去部署数据
st[i] = false;//结束递归,回溯,恢复现场
}
}
}
int main()
{
cin >> n;
dfs(0);
return 0;
}
void dfs(int step)
{
判断边界
尝试每一种可能 for(int i=1;i <= n;i++>)
{
继续下一步 dfs(step+1);
恢复现场
}
返回
}
//模型中的每一种尝试就是一种"拓展"。每次站在盒子面前的时候,其实都有n种拓展,但不是每种拓展都会拓展成功
钥匙丢了,找钥匙的这个过程就和执着的dfs一样。无论从哪个房间开始找都是可以的,可以从主卧,可以从客厅,然后就是对每一个位置挨着挨着的找,不放过任何一个死角,所有的抽屉、储物柜、床上、床下和衣柜等等位置,找不到?换下一个位置继续找,直到找到为止。
温馨提示:把房间找乱了,走到下一个位置找之前,记得还原房间之前的状态,否则家妻可能要家法伺候了。
小明手中有三张卡片分别是1,2,3。现在要把这三张卡片放到三个编号为1,2,3的盒子里,并且每个盒子只能放一张卡片。问现在有多少种不同的放法了?
现在小明走到盒子面前,心想我是先放1号卡片,还是先放2号卡片,还是先放3号卡片了?
很明显,这三种情况都是可取的。为了放卡片的的时候不犹豫,就约定每次走到盒子面前,都是先放1号,再放2号,最后放3号。
放好之后,小明来到2号盒子之前,本来按照之前的顺序是应该按照1,2,3的顺序放。但是小明现在手中只有2号和3号卡片了,于是将2号卡片投到2号盒子中。
现在小明在3号盒子之前,按照之前的约定,应该是1号,2号,3号的顺序放置。但是小明手中现在只有3号卡片了,于是只能将3号卡片投入3号盒子中。
放置好以后,小明再向后走一步,到4号盒子面前,诶?没有4号盒子,确实也应该没有,因为我们的卡片也已经放完了
当小明走到第4号盒子的时候,已经就出现一种排列方式了,即"1,2,3"
是不是到这里就结束了呢?当然不是的,产生一种序列之后,小明应该返回尝试其他可能。现在小明需要退回到3号盒子。
好了,小明退回到3号盒子面前了,需要取回3号盒子中的卡片,尝试能不能放其他的卡片从而产生一种新的序列了?
取回3号卡片以后,小明想再往3号盒子里面放卡片的时候,发现手中仍然只有3号卡片,没有别的选择,于是不得不再退回一步,退回到2号盒子面前。
小明退回到2号盒子后,取回2号卡片。现在小明手中有两张卡片了,分别是2号和3号卡片。
按照之前的约定,现在需要往2号盒子放3号卡片了(上次已经放过3号卡片了)。放好之后,小明来到3号盒子面前。将手中仅有的2号卡片放到3号盒子中,此时生成新的序列"1,3,2"
按照刚才的做法去模拟,就可以得到"2,1,3",“2,3,1”,“3,1,2”,“3,2,1”
通过这个例子,我们应该可以清楚,深度优先遍历的关键在于"当下该如何做"。至于"一下步如何做"则与"当下该如何做"的逻辑一样。
比如结合函数模型和小明放卡片的样例来看:
现在我们要拿捏的就是按照什么样的顺序一步一步解决放好当前这个数据。对于下一步,只用考虑调用函数递归进去就好。
dfs(0);