深度优先搜索---一个懵逼的东东
深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次.(来自百度百科)
先解释以下深度:
深度指在搜索的过程中沿着一条路一直向下进行,直到这条路没有下一个节点停止,然后返回到上一步接着进行上述操作
所以深度优先搜索的整体结构就是:
1.递归2.剪枝
可能这样说有点不清楚,举个例子
打印1-4的全排列开头为1和2的所有序列;
1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
2 1 3 4
2 1 4 3
2 3 1 4
2 3 4 1
2 4 1 3
2 4 3 1
上述是由编译器进行深搜算法打印的1-4的全排列开头为1和2的所有序列
下面是运行的代码
#include<bits/stdc++.h> using namespace std; int n,pd[100],used[100];//pd是判断是否用过这个数 void print()//输出函数 { int i; for(i=1;i<=n;i++) printf("%5d",used[i]);//保留五位常宽 cout<<endl; } void dfs(int k)//深搜函数,当前是第k格 { int i; if(k==n) //填满了的时候 { print();//输出当前解 return; } for(i=1;i<=n;i++)//1-n循环填数 { if(!pd[i])//如果当前数没有用过 { pd[i]=1;//标记一下 used[k+1]=i;//把这个数填入数组 dfs(k+1);//填下一个 pd[i]=0;//回溯 } } } int main() { cin>>n; dfs(0);//注意,这里是从第0格开始的! return 0; }
我们通过上述举例以及举例所用代码来进行分析:
比如我们发现输出的第一行是1 2 3 4,第二行是1 2 4 3
为什么一行会输出四个数字?是因为剪枝,即在递归函数中下一个判定条件,也就是深搜搜索到这一步已经没有路能走了
上边解释剪枝可能有点笼统,简单的说就是走到这,不能往下走了
为什么要有剪枝呢?很显然根据剪枝的定义,就是避免遍历的时候越界,或者去掉影响结果的因素
第二个问题,为什么第一行是1 2 3 4,而第二行是1 2 4 3?
因为在其中进行递归操作,在这先说一个标记数组,这个数组作用是,对所已经便利过的数进行标记,下来我们看下标记数组的记录过程
我们开始运行程序输入4:
进入dfs(0),dfs()中系数表示已经确定几个数的输出,下来遍历1-4发现pd[1]=0,标记1,即pd[1]=1,used[1]=1
下来进行dfs(1)遍历1-4发现used[1]=1,跳过,下一个pd[2]=0,标记2,即pd[2]=1,used[2]=2
依次一直到dfs(4)遍历时发现,满足剪枝条件,输出第一行1 2 3 4
下来进行递归的回溯操作,即在dfs(3)的时候我们标记pd[4]=1;现在回溯pd[4]=0,这样在dfs(3)中的for遍历结束
下来到了dfs(2)是for遍历到i=3的时候进行的上面的dfs(3)的递归结束,所以回溯pd[3]=0,此时dfs(2)中的i=3结束,进行i=4遍历的pd[i]=0,所以used[2+1]=4,即pd[4]=1;
下来进行dfs(2)是i=4的dfs(3)递归,当i=3时pd[i]=0,所以used[3+1]=3,pd[3]=1,下来进行dfs(4)递归
此时达到剪枝边界,进行输出,得到1 2 4 3
然后下来进行..........
算了不写了,太累了,剩下递归,剪枝,回溯按照上面的步骤进行即可
简单的说就是
dfs(k) { if(k==n)剪枝
for()遍历所有,和上面if没关系 { 如果未标记 { 标记 dfs(k+1)递归 回溯 } } }
如果遍历的是图,那么就是遍历相同深度未标记的子节点,具体题目中如何使用看下https://www.cnblogs.com/RE-TLE/category/1531861.html里面的题目以及题解
以上来自一个练习时间长达一年的蒟蒻Acmer,有事您Q我,我一直在de