版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
深度优先在全排列上的应用
问题简述
N个连续自然数的全排列。
变形:
有N张卡片(1,2,3,4,…,n),得出所有排列的可能。
排列到 N 个盒子中
想法一
在第一时间想到的肯定是枚举法,用 for 循环来遍历所有可能
每一个盒子都有N种可能,那么用N层,最后判断N个数各不重复即可。
这个时间复杂成都是O(N^9),会占用大量的时间,而且编写代码也变得非常复杂。
题目解读
- 有N个数字,每个盒子都有N种可能性放入
- 人为规定:小的优先放入
- 判断卡片是否已经被放入
想法二
我最开始还是想到了for循环
大概的想法是:
//第一个盒子N种可能
当前位置=1;
for(int i=1;i<=N;i++){
if(没被放入){
放入当前盒子
当前位置=2;//下一个
//第二个盒子N种可能
for(int i2=1;i2<=N;i2++){
if(没有放入){
放入当前盒子
当前位置=3;//下一个
........
//第N个盒子
for(int iN=1;iN<=N;iN++){....结束的标志}
当前位置-1;//返回 N-1层
}
}
当前位置-1; //for2层结束返回for1层
}
}
//程序结束
显然,这并没有减少编程的负担,而且一旦更改N的数值,就得修改代码…反正我不愿意这么干哈哈。
分析这段代码,可以发现每一层for循环内执行的任务是一样的:找一个没有放入的最小的数放入当前,然后跳入下一层,排完一组排列后再一层一层跳回。一组排序的结束标志:当前位置跳到了N+1(N+1本身是不存在的)
想法三
由此想到“递归”编程:
- 重复的代码段处理相同的任务
- 有结束标志
一层一层返回也可以联想到递归,递归函数种的 return 返回到上一次调用本函数的位置,即返回了上一层。
int box[10],book[10],N=5;
//解决第step个盒子前的组合问题
void df(int step){
//结束标志
if(step == N+1){
//打印盒子数组种的值
for(int i=1;i<=N;i++)
cout<<box[i];
cout<<endl;
return;//返回上一层
}
//遍历N种可能
for(int i=1;i<=N;i++){
//判断是否被占用,判断是否被标记
if(book[i]!=1){
//放入
box[step] = i;
//置标记
book[i]=1;
//跳入下一层
df(step +1);
//跳回本层
book[i]=0;//拿出卡片,继续循环
}
}
return;//跳回上一层
}
深度优先搜索
以上思想其实就是深度有限搜索的基本模型
void dfs(int step){
判断边界
尝试每一种可能{
继续下一步 ;
}
全部完成返回上一步
}
我个人觉得递归其实还是挺难的,主要在于抓住传入的参数,重复的代码段,和结束条件。