BFS算法的优化 双向宽度优先搜索
2024-10-20 00:50:00
双向宽度优先搜索 (Bidirectional BFS) 算法适用于如下的场景:
- 无向图
- 所有边的长度都为 1 或者长度都一样
- 同时给出了起点和终点
以上 3 个条件都满足的时候,可以使用双向宽度优先搜索来求出起点和终点的最短距离。
算法描述
双向宽度优先搜索本质上还是BFS,只不过变成了起点向终点和终点向起点同时进行扩展,直至两个方向上出现同一个子节点,搜索结束。我们还是可以利用队列来实现:一个队列保存从起点开始搜索的状态,另一个保存从终点开始的状态,两边如果相交了,那么搜索结束。起点到终点的最短距离即为起点到相交节点的距离与终点到相交节点的距离之和。
Q.双向BFS是否真的能提高效率?
假设单向BFS需要搜索 N 层才能到达终点,每层的判断量为 X,那么总的运算量为 X ^ NXN. 如果换成是双向BFS,前后各自需要搜索 N / 2 层,总运算量为 2 * X ^ {N / 2}2∗XN/2。如果 N 比较大且X 不为 1,则运算量相较于单向BFS可以大大减少,差不多可以减少到原来规模的根号的量级。
from collections import deque def doubleBFS(start, end):
if start == end:
return 1 # 分别从起点和终点开始的两个BFS队列
startQueue, endQueue = deque(), deque()
startQueue.append(start)
endQueue.append(end)
step = 0 # 从起点开始和从终点开始分别访问过的节点集合
startVisited, endVisited = set(), set()
startVisited.add(start)
endVisited.add(end)
while len(startQueue) and len(endQueue):
startSize, endSize = len(startQueue), len(endQueue)
# 按层遍历
step += 1
for _ in range(startSize):
cur = startQueue.popleft()
for neighbor in cur.neighbors:
if neighbor in startVisited: # 重复节点
continue
elif neighbor in endVisited: # 相交
return step
else:
startVisited.add(neighbor)
startQueue.append(neighbor)
step += 1
for _ in range(endSize):
cur = endQueue.popleft()
for neighbor in cur.neighbors:
if neighbor in endVisited:
continue
elif neighbor in startVisited:
return step
else:
endVisited.add(neighbor)
endQueue.append(neighbor) return -1
学习建议
Bidirectional BFS 掌握起来并不是很难,算法实现上稍微复杂了一点(代码量相对单向 BFS 翻倍),掌握这个算法一方面加深对普通 BFS 的熟练程度,另外一方面,基本上写一次就能记住,如果在面试中被问到了如何优化 BFS 的问题,Bidirectional BFS 几乎就是标准答案了。
参考资料
https://www.geeksforgeeks.org/bidirectional-search
应用例题
Shortest Path in Undirected Graph
Knight Shortest Path
Knight Shortest Path II
最新文章
- TCP四步挥手的各种状态转换图
- Atitit usrQBK13 html dsl 规范与解决方案
- 在SQL Server 2012中实现CDC for Oracle
- HDU 5122 K.Bro Sorting(2014北京区域赛现场赛K题 模拟)
- datebox清除按钮,datebox加上清除按钮,easyui datebox加上清除按钮
- GS1已分配给国家(地区)编码组织的前缀码
- 【leetcode】Merge k Sorted Lists(按大小顺序连接k个链表)
- (原)Matlab的svmtrain和svmclassify
- leetcode Swap Nodes in Pairs python
- BZOJ 1019: [SHOI2008]汉诺塔( dp )
- 一个基于原生JavaScript开发的、轻量的验证码生成插件
- php解决高并发问题
- java 方法超时
- 「ZJOI2016」旅行者 解题报告
- 阿里技术专家详解Dubbo实践,演进及未来规划
- Eisenstein's criterion
- 改装原生的dialog
- JAVA面试精选【Java基础第二部分】
- 五、excel末尾补0和开头补0
- 【原创】c# Winform 使用 web 的UrlEncode/UrlDecode 方法
热门文章
- shell(三)if流程控制
- 记录一次在生成数据库服务器上出现The timeout period elapsed prior to completion of the operation or the server is not responding.和Exception has been thrown by the target of an invocation的解决办法
- magic模块 :Exception Value:failed to find libmagic. Check your installation
- Maven 教程(3)— Maven仓库介绍与本地仓库配置
- Linux内核模块管理命令
- python入门之名称空间
- [原创]开源跨平台大型网络端口扫描器K8PortScan(支持批量A段/B段/C段/IP列表)
- [转帖]Kubernetes的部署策略
- C++贪心算法实现部分背包问题
- Python 3 + Selenium 3 实现汉堡王客户调查提交