算法第二章·深度优先搜索&广度优先搜索(DFS&BFS)
在本章中,我会围绕几个有向图、无向图为你展开叙述DFS和BFS的原理和代码实现
DFS介绍
DFS是一个遍历算法,其基本原理就是“尝试”,即每次搜索遇到尽头就回溯,这里之后会去讲解。DFS的本质就是一个递归(你也可以说绝大多数算法都是递归),通过不断地搜索去遍历全图。DFS的时间复杂度大致为O(V^2)。
DFS过程
有向图的遍历
如下图1-1是一个有向图

图1-1
假设我们选取节点E作为起始节点(你也可以选择其它的节点作为起始节点,但不一定能够遍历全图),我们会发现从E可以去到A点,那么我们就将E点标记为已遍历就不去管它了。到达A点后依旧只能前往C点我们就跳转到C点,同理我们可以得到遍历序列EACD,可是在D点的时候有两条路径该选哪条呢?都不重要,两条都可以(从这里可以看出DFS遍历路径多样且不唯一的特性)。但是我们发现在B点时就发现唯一可以去的C点已经被标记过了,所以我们就回溯,即从B点重新跳转回D点查看有没有新路径。当我们在D点时我们会发现之前还有点F没有被标记,且D→F是通的,那么这个时候我们就遍历点F。当我们在点F时发现没有路径可走,那就接着回溯。
最终我们会得到遍历序列EACDBF
无向图的遍历
同理,我们来看一下无向图的遍历。如下图1-2是一个无向图

图1-2
假设我们选取C作为搜索起点(本图中任意节点均可以,遍历序列不唯一)并将其标记为已搜索,我们发现从C点可以去到A、D、B三个节点。假设我们先搜索A节点,发现A没有其它直接与其相通的节点我们就进行回溯操作回到C点。接下来搜索D点,同理回溯。然后我们搜索B点,发现B有后继F点(即直接与F点相连),我们继续搜索。最终可以得出搜索序列CADBFE
BFS介绍
BFS是一种以类似病毒扩散(也就是我们常说的一传十十传百)的遍历算法去进行搜索的,BFS时间复杂度相较于DFS的时间复杂度差不太多,都是O(V^2)。具体搜索方式见下
BFS过程
如下图2-1是一个表

图2-1
我们假设左上角为搜索起点,要求遍历1范围内的格子。我先进行标记,接下来BFS(广度优先搜索算法)会依照“图2-2→图2-3→图2-4→图2-5→图2-6→图2-7→图2-8→图2-9→图2-10→图2-11”顺序去层层递进搜索

图2-2

图2-3

图2-4

图2-5

图2-6

图2-7

图2-8

图2-9

图2-10

图2-11
例题
T1:P1162 填涂颜色
题意解析:你需要在数字“1”圈画的闭合圈内去将其它格子填充,使得最终输出闭合圈内全部数字为1
依据题意我们可以很明显的看出这是一道典型的BFS模板题(题目数据不好,DFS也可以做,我们这里只讲解BFS的方式,想要尝试DFS的可以自己去试),我们先创建一个队列来存储已经遍历过的点,防止重复遍历造成死循环,然后四个方向依次判断递归即可
code:
#include<bits/stdc++.h> using namespace std; const int M=31; bool vis[M][M],maps[M][M]; int n,m,a,b,c; queue<int> q; void bfs(int x,int y) { vis[x][y]=1; q.push(x),q.push(y); while(!q.empty()) { int w=q.front(); q.pop(); int e=q.front(); q.pop(); if(!maps[w+1][e]&&w!=n&&!vis[w+1][e]) vis[w+1][e]=1,q.push(w+1),q.push(e); //四个方向进行搜索 if(!maps[w-1][e]&&w!=1&&!vis[w-1][e]) vis[w-1][e]=1,q.push(w-1),q.push(e); if(!maps[w][e+1]&&e!=n&&!vis[w][e+1]) vis[w][e+1]=1,q.push(w),q.push(e+1); if(!maps[w][e-1]&&e!=1&&!vis[w][e-1]) vis[w][e-1]=1,q.push(w),q.push(e-1); } } int main() { cin>>n; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { cin>>maps[i][j]; if(maps[i][j]) vis[i][j]=1; } } for(int i=1;i<=n;i+=n-1 ) { for(int j=1;j<=n;j++) { if(vis[i][j]) continue; bfs(i,j); } } for(int i=1;i<=n;i+=n-1) { for(int j=1;j<=n;j++) { if(vis[j][i]) continue; bfs(j,i); } } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(!vis[i][j]) cout<<2<<' '; else cout<<maps[i][j]<<' '; } cout<<endl; } return 0; }
T2:P1451 求细胞数量
题意解析:遍历全图,求出被数字包围的数字有多少个
这也是一个BFS模板题,依照模板稍加修改即可
code:
#include<bits/stdc++.h> using namespace std; struct node { int x,y; }; const int N=110; char cell[N][N]; int ans,n,m,dx[4]={-1,1,0,0},dy[4]={0,0,-1,1}; void bfs(int sx,int sy) { queue<node> q; q.push({sx,sy}); cell[sx][sy]='0'; while(!q.empty()) { node t=q.front(); q.pop(); int x=t.x,y=t.y; //枚举(x,y)上下左右四个方向 for(int i=0;i<4;i++) { int xx=x+dx[i],yy=y+dy[i]; if(xx>=1&&xx<=n&&yy>=1&&yy<=m) if(cell[xx][yy]!='0') cell[xx][yy]='0',q.push({xx,yy}); } } } int main() { cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>cell[i][j]; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(cell[i][j]!='0') { ans++; bfs(i,j); } } } cout<<ans; return 0; }
T3:P1030 [NOIP2001 普及组] 求先序排列
题目大意:给出一棵树的中序和后序遍历,求其前序遍历
个人认为这可能不太算是DFS,反而更像递归
code:
#include<bits/stdc++.h> using namespace std; void DFS(string in,string after) { if(in.size()>0) { char ch=after[after.size()-1]; cout<<ch;//找根输出 int k=in.find(ch); DFS(in.substr(0,k),after.substr(0,k)); DFS(in.substr(k+1),after.substr(k,in.size()-k-1)); } return; } int main() { string s,b; cin>>s>>b; DFS(s,b); }
DFS应用
1.解决联通问题
2.解决路径数量问题
3.排列/组合问题

浙公网安备 33010602011771号