算法学习-深度优先搜索
1:总览深度优先搜索
深度优先搜索(缩写DFS)有点类似广度优先搜索,也是对一个连通图进行遍历的算法。它的思想是从一个顶点V0开始,沿着一条路一直走到底,如果发现不能到达目标解,那就返回到上一个节点,然后从另一条路开始走到底,这种尽量往深处走的概念即是深度优先的概念。
2:和广度优先搜索的对比
广度优先搜索(BFS)是按照一个树的层次来完成搜索的。
-
缺点:当树的层次较深&&子节点过多的时候,消耗内存极其严重
解释如下:
假设一个节点衍生出来的相邻节点平均的个数是N个,那么当起点开始搜索的时候,队列有一个节点,当起点拿出来后,把它相邻的节点放进去,那么队列就有N个节点,当下一层的搜索中再加入元素到队列的时候,节点数达到了N^2,你可以想想,一旦N是一个比较大的数&&这个树的层次又比较深,那这个队列就得需要很大的内存空间了。
-
优点:可以寻找无权图的最短路~
深度优先搜索(DFS)在每次搜索的过程中每层仅仅只需要维护一个节点,内存消耗小。但是,刚刚也提到了深度优先搜索是沿着一条路找到黑的,需要找到所有的路径才可以确定最短路径。
- 缺点:难以寻找最优解
- 优点:内存消耗小
3:伪代码+具体例题
/**
*DFS伪代码(递归实现)
*@param n 当前开始搜索的节点
*@param step 当前到达的深度,即任务完成量的一种度量
*/
void DFS(Node n,int step){
//任务完成
if(step == xxx){
//do something 常常为输出/记录当前路径信息
}
for (Node nextNode in n){//遍历跟节点n相邻的节点nextNode
//这个需要根据题意来确定下个节点是否可以访问
if(nextNode可以访问){
//进行一些压栈操作 往往是把当前节点变成不可以访问节点
DFS(nextNode,step+1);
//进行一些弹栈操作 往往是把当前节点变成可以访问节点
}
}
}
#include<iostream>
using namespace std;
bool ldiagonal[400];//左对角线
bool rdiagonal[200];//右对角线
bool cow[200];//列
int cash[15];//缓存
int n;//棋盘大小
int ans;
/*
*@param step 当前到达的深度,即任务完成量的一种度量
*由于问题的特殊性,当前访问节点可以用step代替
*/
void solve(int step){
if(step >= n){//任务是否完成的度量
ans++;
if(ans <= 3){
for(int i = 0;i < n;i++){
cout<<cash[i]<<" ";
}
cout<<endl;
}
else{
return;
}
}
//列
for(int j = 0;j < n;j++){//遍历当前行所有可以访问的列
//查看列是否可以访问
if(!cow[j]&&!rdiagonal[step + 1 + j]&&!ldiagonal[step + 1-j+n]){
cash[step] = j + 1;
//缓存操作,为了在任务完成时候,好记录/输出结果
cow[j] = true;
rdiagonal[step + 1+j] = true;
ldiagonal[step + 1-j+n] = true;
//上面三行为压栈操作,可以看出来是在把当前节点变成不可再次访问节点
solve(step + 1);
//递归操作
cow[j] = false;
rdiagonal[step + 1+j] = false;
ldiagonal[step + 1-j+n] = false;
//上面三行为弹栈操作
}
}
}
int main(){
cin>>n;
solve(0);
cout<<ans;
return 0;
}
最后,总结一下
深度优先搜索的算法需要你对递归有一定的认识,其中最重要的思想就是:抽象!
可以从DFS函数里边看到,DFS里边永远只处理当前状态节点n,而不去关注它的下一个状态。
它通过把DFS方法抽象加上使用递归,使整个逻辑就变得十分的清晰。