图的BFS遍历中标记已访问的节点操作影响效率

在做leetCode 130 Surrounded Regions时一直TLE,但是对照自己的代码更别人的代码也“几乎”相同,最后终于找到问题所在,就是在图的BFS过程中,什么时候对已经访问的节点做标记(这些语句安排在哪里)是会严重影响效率的!

两段代码如下:

    void bfsBoundry1(vector<vector<char>> &board, int i, int j)
    {
        int m = board.size();
        int n = board[0].size();
        queue<pair<int, int>> que;
        que.push({i, j});
        while(!que.empty())
        {
            auto coor_0 = que.front();
            que.pop();
            board[coor_0.first][coor_0.second] = 'B';  //标记为已经访问的节点
            vector<pair<int, int>> coors;
            if(coor_0.first-1 >= 0)
                coors.push_back({coor_0.first-1, coor_0.second});
            if(coor_0.first+1 < m)
                coors.push_back({coor_0.first+1, coor_0.second});
            if(coor_0.second-1 >= 0)
                coors.push_back({coor_0.first, coor_0.second-1});
            if(coor_0.second+1 < n)
                coors.push_back({coor_0.first, coor_0.second+1});
            for(auto a : coors)
            {
                if(board[a.first][a.second] == 'O')
                    que.push(a);
            }
        }
    }

    void bfsBoundry2(vector<vector<char>> &board, int i, int j)
    {
        int m = board.size();
        int n = board[0].size();
        queue<pair<int, int>> que;
        que.push({i, j});
        board[i][j] = 'B';  //标记为已经访问的节点
        while(!que.empty())
        {
            auto coor_0 = que.front();
            que.pop();
            vector<pair<int, int>> coors;
            if(coor_0.first-1 >= 0)
                coors.push_back({coor_0.first-1, coor_0.second});
            if(coor_0.first+1 < m)
                coors.push_back({coor_0.first+1, coor_0.second});
            if(coor_0.second-1 >= 0)
                coors.push_back({coor_0.first, coor_0.second-1});
            if(coor_0.second+1 < n)
                coors.push_back({coor_0.first, coor_0.second+1});
            for(auto a : coors)
            {
                if(board[a.first][a.second] == 'O')
                {
                    que.push(a);
                    board[a.first][a.second] = 'B';  //标记为已经访问的节点
                }
            }
        }
    }

上面两段代码的功能完全一致,就是以某个点开始,进行上下左右的行走,如果走得通(‘O’),则访问,访问之后标记为‘B’。不同之处就在于在什么时候标记已经走过的节点。bfsBoundry1的效率比bfsBoundry2的效率会低很多。原因是1在把节点放到队列时不标记为访问,而出队列时标记为访问;2与此相反。1的问题在于从某个节点开始可能有多条可选择的路径,而这多条路径的下一条路径可能会重叠。如果不在把节点放到队列时及时标记为已访问节点,那么他们可能在后续的访问中向队列中加入重复的节点,导致效率下降!

比如有如下图:

1 2

3 4

使用“bfsBoundry1算法时:

在访问1时,会把2、3放到队列中,此时2、3并没有标记为访问;

当把2出队列时,2标记为访问,此时会把4加入到队列中,4没有标记为访问;

当把3出队列时,3标记为访问,由于4没有标记为访问,所以此时又会把4放到队列中!

使用bfsBoundry2算法不会有上面的问题!

所以,记住一点,当使用BFS对图进行遍历时,一定要在把节点放到队列时,及时将该节点设置为“访问”过状态!

    原文作者:数据结构之图
    原文地址: https://blog.csdn.net/tjy1220646144/article/details/44748639
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞