深度搜索与广度搜索
一、前导
先分别了解一下深度搜索和广度搜索
深度搜索: 深度搜索的遍历过程简单来说就是一路走到底再往回走。例如当前最新节点为 v,那么我们先判断该点是否还有其他未搜索过的边,如果有直接进入第一个发现的新节点。 但如果没有发现新的边了,就回到上一节点再探寻上一节点是否有其他边,如果没有就一直回退直到所有节点探索完毕或者达到目的。
广度搜索: 广度搜索就是一个地毯式的扩展搜索, 以初始节点为中心向外扩散搜索。例如初始节点为 v,那么先遍历 v 可以达到的所有边,然后再以此遍历达到的新节点可以到达的边。
了解完深度和广度的基本思路后,我们分别做一个具体的介绍
二、 深度搜索DFS
1.DFS的运用
我们首先得知道DFS的代码实现是基于递归实现的,并且了解DFS最重要的方法—回溯法。回溯法很好理解,就是使用完一个点后,这个点没有了作用或者不符条件了,那么就 “清扫现场” 即清除当前节点的访问状态。下图就是一个回溯搜索过程:
列题:
迷宫 (题号待补)
就是在一个二维矩阵中寻找出路
马走日 (题号待补)
就是象棋中马的移动,比迷宫稍复杂一点,以为马的访问方向是一个日型。
2. DFS的基础模板
- 邻接矩阵构图的模板 (连通图)
const int maxn = 1100;
int g[maxn][maxn]; //矩阵定义
bool vis[maxn]; //访问记录
void DFS(int now, int n) //now 表示当前节点号, n为矩阵大小
{
if(vis[now]) return; //如果节点访问过,那么终止下搜
vis[now] = true; //如果没有访问过就标记访问
//printf("%d",now); 输出访问路径 可以此观察搜索顺序
for(int i = 0; i < n; i++)
if(g[now][i]) //如果 now 到 i 有边
DFS(i,n); //把 i 当作新的出发节点继续搜索
//回溯
vis[now] = false;
}
- 非联通图
非联通图的实现就得多一个条件,记录初始节点start。 当start的所有联通点都遍历过后,再判断是否有节点没有遍历,如果有那么就是有非联通的图,那么我们就把该点作为新的 start继续遍历。
const int maxn = 1100;
int g[maxn][maxn]; //矩阵定义
bool vis[maxn]; //访问记录
void DFS(int now, int n,int start)
{
if(vis[now]) return;
vis[now] = true;
//printf("%d",now); 输出访问路径 可以此观察搜索顺序
for(int i = 0; i < n; i++)
if(g[now][i])
DFS(i,n,start);
//在一个初始节点的所有连接点都探索完
//判断是否还有点没有探索
for(int i = 0; i < n && now == start; i++)
if(vis[i] == 0)
DFS(i,n,i);
//回溯
vis[now] = false;
}
- 简易模板
//状态可以有更多
void dfs(int now, int n, int start, int depth) //当前点、大小、起始点、深度
{
//访问条件
if(vis[now]) return;
//标记访问
vis[now] = true;
//特判条件
if(now == end) //举个列子到达了终点
{
//需要的操作
cout << "到达目的了" << endl;
}
//循环遍历
for(int i = 0; i < n; i++)
if(g[now][i])
{
//需要的操作,如记录或者输出
DFS(i,n,start,depth++);
}
//回溯
vis[now] = 0;
}
3.剪枝
在题目中除了状态和目的条件的限制外,还有重要的时间限制。如果没有限制的对所有节点访问,在数据量大的时候可能就会出现超时了。例如走迷宫,当迷宫是一个20×20的矩阵时,访问量仅有400个节点,但如果时3000×3000时访问量就到达了9000000的大数目必然会超时。