广度优先搜索(BFS)的一个(重要!)细节(可能因此卡题)

引言:

为了一个广度优先搜索的细节有必要水一整篇文章?有必要。

这个细节非常重要,以至于我在切Leetcode某一题的时候,明明和答案的高效率通过的代码相差无几,逻辑毫无错误,STL使用相同,但仍然有几个测试点卡不过去。

题目来源:200.岛屿数量


我原来的代码:

class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
       // vector<vector<bool>> visited(grid.size(),vector<bool>(grid[0].size(),false));
        //int dx[4] = {1,0,-1,0};
        //int dy[4] ={0,-1,0,1};
        int res = 0;
        int r= grid.size();
        int c = grid[0].size();
        for(int i = 0;i<r;++i){
            for(int j  =0;j<c;++j){
                if(grid[i][j] == '0')continue;
                queue<pair<int,int>> q;
                q.push({i,j});
                res ++;
                grid[i][j] = '1';
                while(!q.empty()){
                    int x = q.front().first;
                    int y = q.front().second;
                    q.pop();
                    grid[x][y] = '0';
                    /*
                    for(int k = 0;k<4;++k){
                        int xx = x+dx[k];
                        int yy = y+dy[k];
                        if(xx<0||xx>=grid.size() ||yy<0||yy>=grid[0].size()||grid[xx][yy]=='0'){
                            continue;
                        }
                        q.push(make_pair(xx,yy));
                    }*/
                    if(x-1>=0 && grid[x-1][y]=='1')q.push({x-1,y});
                    if(y-1>=0 &&grid[x][y-1]=='1')q.push({x,y-1});
                    if(x+1<r&&grid[x+1][y]=='1')q.push({x+1,y});
                    if(y+1<c&&grid[x][y+1]=='1')q.push({x,y+1});
                }
            }
        }
        return res;
    }
};

代码运行后,总是超时。

我做了许多猜测与尝试,排除了  另外开visited数组make_pairdx[]与dy[]、甚至访问vector的size()等可能导致超时的因素。

但是都没有太大的效果。

于是排除所有可能,真相就只有一个了——

先来看看答案代码:

class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
        int nr = grid.size();
        if (!nr) return 0;
        int nc = grid[0].size();

        int num_islands = 0;
        for (int r = 0; r < nr; ++r) {
            for (int c = 0; c < nc; ++c) {
                if (grid[r][c] == '1') {
                    ++num_islands;
                    grid[r][c] = '0';
                    queue<pair<int, int>> neighbors;
                    neighbors.push({r, c});
                    while (!neighbors.empty()) {
                        auto rc = neighbors.front();
                        neighbors.pop();
                        int row = rc.first, col = rc.second;
                        if (row - 1 >= 0 && grid[row-1][col] == '1') {
                            neighbors.push({row-1, col});
                            grid[row-1][col] = '0';
                        }
                        if (row + 1 < nr && grid[row+1][col] == '1') {
                            neighbors.push({row+1, col});
                            grid[row+1][col] = '0';
                        }
                        if (col - 1 >= 0 && grid[row][col-1] == '1') {
                            neighbors.push({row, col-1});
                            grid[row][col-1] = '0';
                        }
                        if (col + 1 < nc && grid[row][col+1] == '1') {
                            neighbors.push({row, col+1});
                            grid[row][col+1] = '0';
                        }
                    }
                }
            }
        }

        return num_islands;
    }
};
作者:LeetCode
链接:https://leetcode-cn.com/problems/number-of-islands/solution/dao-yu-shu-liang-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

对比可以发现,答案与我的代码里面一个看似没什么影响的差别在于:

我的代码是从队列中弹出节点时才把这个节点标记为<已访问>,而答案代码里是在push进队列的时候就标记其为<未访问>

看似没有什么逻辑问题啊。

但是,

我仔细

仔细

想了想。

这实际上导致的效率差别是巨大的!

比如,在访问cur节点的时候,把一个B节点push了进队列(也就是说B节点是cur节点的邻居节点),

如果我此刻没有标记其为<已访问>的话,

那么我在访问队列中排在B前面的节点cur2时(即在cur之后,B之前,被push进队列的节点),

如果我在cur2的邻居节点里看到了B,此刻由于B还没有出队列,所以它目前未被标记为<已访问>

那么,此时B又会再一次被push进队列里面!!!

也就是说,一个节点会被访问多次,那运行时间固然就翻倍了。

由此可见这个细节的重要性之大。

(如果比赛的时候因为这个细节卡题了的话,我想我就不会痛失倒一了吧。)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
深度优先遍历(DFS)和广度优先遍历(BFS)都是常见的图遍历算法。 在做时,我们可以先根据目需求选择使用 DFS 还是 BFS,然后按照以下步骤进行: DFS: 1. 确定起点和终点; 2. 定义一个 visited 数组,记录每个节点是否被访问过; 3. 从起点开始进行 DFS 遍历; 4. 在遍历过程中更新 visited 数组,防止重复访问; 5. 如果需要,可以记录遍历路径或者其他信息。 BFS: 1. 确定起点和终点; 2. 定义一个 visited 数组,记录每个节点是否被访问过; 3. 定义一个队列,将起点入队; 4. 进行 BFS 遍历,每次从队列中取出一个节点,然后将其未访问过的相邻节点入队; 5. 在遍历过程中更新 visited 数组,防止重复访问; 6. 如果需要,可以记录遍历路径或者其他信息。 在实际做中,我们需要结合具体问来选择使用 DFS 还是 BFS,比如: - 如果需要求最短路径,则通常使用 BFS; - 如果需要求所有可能的路径,则通常使用 DFS; - 如果需要搜索整个图,或者需要找到一些特定信息,则可以使用 DFS 或 BFS。 需要注意的是,在使用 DFS 或 BFS 时,可能会遇到一些问,比如: - 图中可能存在环,需要避免重复访问; - 图可能不连通,需要对每个连通分量都进行遍历; - 图可能非常大,需要考虑优化算法。 因此,在做时,我们需要仔细分析目要求和数据特点,选择合适的算法并注意细节
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咖啡咖_CoffCa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值