代码随想录算法训练营第四十天| 深搜与广搜理论基础以及模板代码

今天是算法学习第四十天,主要的学习内容是深度优先搜索和广度优先搜索,以及对于模板题的讲解。

深度优先搜索(DFS)

深搜其实原理就是先往一个方向搜索,直到遇到终止条件再回头。所以深度优先搜索的关键就是递归和回溯。

对于深搜代码的模板,其实和回溯是很相似的。我们首先确定递归函数的参数和返回值,然后确定递归函数的终止条件,最后确认对当前节点的处理方式。具体框架如下所示:

void dfs(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本节点所连接的其他节点) {
        处理节点;
        dfs(图,选择的节点); // 递归
        回溯,撤销处理结果
    }
}

广度优先搜索(BFS) 

广度优先搜索的思路是以起始点为中心一圈一圈地进行搜索,一旦遇到终点,记录之前走过的节点就是一条最短路。所以,广搜适合解决两个点之间的最短路径问题。

对于广度优先搜索的代码实现,一般是使用队列这一数据结构进行实现。模板代码如下所示:

int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 表示四个方向
// grid 是地图,也就是一个二维数组
// visited标记访问过的节点,不要重复访问
// x,y 表示开始搜索节点的下标
void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {
    queue<pair<int, int>> que; // 定义队列
    que.push({x, y}); // 起始节点加入队列
    visited[x][y] = true; // 只要加入队列,立刻标记为访问过的节点
    while(!que.empty()) { // 开始遍历队列里的元素
        pair<int ,int> cur = que.front(); que.pop(); // 从队列取元素
        int curx = cur.first;
        int cury = cur.second; // 当前节点坐标
        for (int i = 0; i < 4; i++) { // 开始想当前节点的四个方向左右上下去遍历
            int nextx = curx + dir[i][0];
            int nexty = cury + dir[i][1]; // 获取周边四个方向的坐标
            if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;  // 坐标越界了,直接跳过
            if (!visited[nextx][nexty]) { // 如果节点没被访问过
                que.push({nextx, nexty});  // 队列添加该节点为下一轮要遍历的节点
                visited[nextx][nexty] = true; // 只要加入队列立刻标记,避免重复访问
            }
        }
    }

}

99. 岛屿数量

题目链接:99. 岛屿数量 (kamacoder.com)

这个题目使用深搜和广搜都可以解决。我们先聊一聊深搜的做法,其实就是往四周进行遍历,由于这是一个无向图,为了防止回环的情况,我们需要设置一个数组用来存储每个点是否走过。具体代码实现如下所示:
 

#include<iostream>
#include<vector>
using namespace std;
int dis[4][2]={0,1,1,0,-1,0,0,-1};
void dfs(vector<vector<int>> &graph,vector<vector<bool>> &visited,int x,int y)
{
    if(visited[x][y]==1||graph[x][y]==0) return;
    visited[x][y]=1;
    for(int i=0;i<4;i++)
    {
        int nextx=x+dis[i][0];
        int nexty=y+dis[i][1];
        if(nextx<0||nexty<0||nextx>=graph.size()||nexty>=graph[0].size()) continue;
        dfs(graph,visited,nextx,nexty);
    }
}
int main()
{
    int m,n;
    scanf("%d %d",&m,&n);
    vector<vector<int>> graph(m+1,vector<int>(n+1,0));
    for(int i=0;i<m;i++)
    {
        for(int j=0;j<n;j++)
        {
            scanf("%d",&graph[i][j]);
        }
    }
    vector<vector<bool>> visited(m+1,vector<bool>(n+1,false));
    int result=0;
    for(int i=0;i<m;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(!visited[i][j]&&graph[i][j]==1)
            {
                result++;
                dfs(graph,visited,i,j);
            }
             
        }
    }
    cout<<result;
}

除此之外,广搜也可以解决这个题目。需要注意的是,广搜代码一般需要在内部判断节点的可达性,并将存储遍历情况的数组赋值为1.这样可以有效降低时间复杂度。具体代码实现如下所示:
 

#include<iostream>
#include<queue>
#include<vector>
using namespace std;
int dis[4][2]={0,1,1,0,-1,0,0,-1};
void bfs(vector<vector<int>> &graph,vector<vector<bool>> &isvisited,int x,int y)
{
     
    queue<pair<int,int>> que;
    que.push({x,y});
    while(!que.empty())
    {
        pair<int,int> point=que.front();
        que.pop();
        for(int i=0;i<4;i++)
        {
            int nextx=point.first+dis[i][0];
            int nexty=point.second+dis[i][1];
            if(nextx<0||nexty<0||nextx>=graph.size()||nexty>=graph[0].size()) continue;
            if(isvisited[nextx][nexty]==0&&graph[nextx][nexty]==1)
            {
                que.push({nextx,nexty});
                isvisited[nextx][nexty]=1;
            }
        }
    }
}
int main()
{
    int m,n;
    cin>>m>>n;
    vector<vector<int>> graph(m+1,vector<int>(n+1,0));
    vector<vector<bool>> isvisited(m+1,vector<bool>(n+1,0));
    for(int i=0;i<m;i++)
    {
        for(int j=0;j<n;j++)
        {
            scanf("%d",&graph[i][j]);
        }
    }
    int result=0;
    for(int i=0;i<m;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(isvisited[i][j]==0&&graph[i][j]==1) 
            {
                result++;
                bfs(graph,isvisited,i,j);
            }
        }
    }
    printf("%d\n",result);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值