广度优先遍历
常用的解决无权重最短路径问题方法,其核心在于使用队列“先进先出”的特点,能快速找到最靠近起始节点的目标节点。
问题
由于各节点顺序进队出队,在查找到目标节点后很难追溯最短路径。
解决方案
参考Dijkstra算法,额外增加parents散列表,记录下能使入队节点被最早发现的父节点,即当节点入队时候,一起更新入队节点的父节点信息,以方便后续路径追溯。
代码
路径图如下
A--1--C
6 / | |1
start |3 final
2\ | /5
B
def creat_graph():
"""字典嵌套字典实现带权重有向路径图"""
graph = dict()
graph["start"] = dict()
graph["start"]['a'] = 6
graph["start"]['b'] = 2
graph["a"] = dict()
graph["a"]['c'] = 1
graph["c"] = dict()
graph["c"]['fin'] = 1
graph["b"] = dict()
graph["b"]['a'] = 3
graph["b"]['fin'] = 5
graph["fin"] = dict()
return graph
创建父节点散列表
def parents_table():
# 创建存储父节点的散列表
parents = dict()
parents['a'] = None
parents['b'] = None
parents['c'] = None
parents['fin'] = None
return parents
BFS实现
def BFS(graph, start, end, parents):
"""广度优先遍历,搜寻最短路径"""
from collections import deque # 创建搜索队列
search_queue = deque()
# 队列中添加起始点,并记录邻居节点父节点
for node in graph[start]:
if not parents[node]:
parents[node] = 'start'
search_queue.append(node)
searched = [] # 记录已查找节点
while search_queue:
node = search_queue.popleft()
if node not in searched:
if node == end: # 找到目标返回父节点列表,用以追溯最终路径
return node, parents
else:
searched.append(node) # 更新已搜索列表
for n in graph[node]: # 记录达到节点的最近路径的父节点
if not parents[n]:
parents[n] = node
search_queue.append(n) # 邻居节点入队
return False
路径追溯
def find_start(parents, key):
res = []
while parents[key] != 'start':
res.append(key)
key = parents[key]
res.append(key)
res.append("start")
res.reverse()
for i in res:
if i == 'fin':
print(i)
else:
print(i, "-->", end='')
目标:查找从“start”–>“fin”的最短路径
def main():
# 创建路径图和父节点散列表
graphTable = creat_graph()
parents = parents_table()
item, parentsTable = BFS(graphTable, 'start', 'fin', parents)
# 最终路径追溯
find_start(parentsTable, 'fin')
if __name__ == '__main__':
main()