A3阶段 第七课 深度优先搜索
2025年6月28日
学习时长
约1.5小时
难度
A3阶段/CSP-J
标签
数据结构
讲师
王骏峣
深度优先搜索
利用递归函数来实现暴力枚举
将问题需要决策的部分分为若干个阶段
例如想要输出所有长度为n的排列,分为n个阶段,第i个阶段决定排列的第i个位置,对于每个阶段,都可以选择1~n之间的数填入
因此,可以设计一个递归算法:从第一个位置开始,用循环枚举当前位置的值,再往下一个位置递归
代码结构
void dfs(int p){
if(p == n + 1){
判断当前a数组是否为一个阶段,若是,则输出
return;
}
for(int i = 1;i <= n;i ++){
a[p] = i;
dfs(p + 1);
}
}
主函数调用:dfs(1);
上述代码的复杂度过高,为O(nn)
注意到如果前面的序列里出现了重复的数字,那么无论后面填入什么都不可能是排列
优化思路:一旦发现再往后搜也不会得到合法解的时候,提前返回(搜索的可行性剪枝)
剪枝实现
用vis数组标记第i个数字是否被使用
代码结构
int vis[N];
void dfs(int p){
if(p == n + 1){
for(int i = 1;i <= n;i ++)
cout << a[i] << " ";
cout << endl;
return;
}
for(int i = 1;i <= n;i ++){
if(vis[i] == 0){
a[p] = i;
vis[i] = 1;
dfs(p + 1);
vis[i] = 0;
}
}
}
主函数调用:dfs(1);
dfs结束后,需要枚举将下一个数字填入第p个位置,先将当前填入的数字i标记撤销,这一步称为回溯
若将状态与状态之间的联系看作树,
深度优先搜索对状态的遍历顺序和
树的深度优先遍历是一致的
这也是剪枝的名字由来,即
剪去搜索树上的一部分枝条
深度优先搜索的局限性
- 深度优先搜索本质上也是暴力枚举,时间复杂度偏高
- 只适用于数据规模较小的题目
- 或者用于取部分分