算法第二章·深度优先搜索&广度优先搜索(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.排列/组合问题

posted @ 2024-05-19 16:09  Jared-Aya  阅读(44)  评论(0)    收藏  举报