图的BFS遍历中标记已访问的节点操作影响效率
2015-03-30 11:42
267 查看
在做leetCode 130 Surrounded Regions时一直TLE,但是对照自己的代码更别人的代码也“几乎”相同,最后终于找到问题所在,就是在图的BFS过程中,什么时候对已经访问的节点做标记(这些语句安排在哪里)是会严重影响效率的!
两段代码如下:
上面两段代码的功能完全一致,就是以某个点开始,进行上下左右的行走,如果走得通(‘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对图进行遍历时,一定要在把节点放到队列时,及时将该节点设置为“访问”过状态!
两段代码如下:
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对图进行遍历时,一定要在把节点放到队列时,及时将该节点设置为“访问”过状态!
相关文章推荐
- .net中利用oracle产品自带的数据访问组件(Oracle.DataAccess.dll)提升批量更新操作的执行效率
- 测试orcale中索引对表的查询和操作效率 影响
- jQuery基础教程之DOM操作-遍历节点-parents()方法
- jQuery基础教程之DOM操作-遍历节点-siblings()方法
- jQuery基础教程之DOM操作-遍历节点-next()方法
- 建立JSP操作以提高数据库访问的效率
- 二叉树的各种操作 先序 中序 后续 层次 遍历 求树高度 节点深度 知先序中序求后续 二叉排序树
- jQuery基础教程之DOM操作-遍历节点-children()方法
- 【Android Training - Connectivity】优化下载的效率[Lesson 1 - 看无线电波如何影响网络操作]
- jQuery基础教程之DOM操作-遍历节点-find()方法
- .net中利用oracle产品自带的数据访问组件(Oracle.DataAccess.dll)提升批量更新操作的执行效率
- javascript操作dom 建立 增加 删除 克隆 访问节点
- javascript操作dom 建立 增加 删除 克隆 访问节点
- jQuery基础教程之DOM操作-遍历节点-parent()方法
- SQL Server 2005 XML 操作总结(五)元素(节点)操作——修改、删除、移动、遍历操作
- 结构对齐对结构成员的访问效率影响的测试。
- 简单的XML文件操作(xml文件生成,节点追加、遍历、修改、删除)
- 如何建立JSP操作用以提高数据库访问效率
- jQuery基础教程之DOM操作-遍历节点-prevAll()方法
- jQuery基础教程之DOM操作-遍历节点-filter()方法