在做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对图进行遍历时,一定要在把节点放到队列时,及时将该节点设置为“访问”过状态!