前几天观看了B站up主:正月点灯笼 的视频,里面讲到了BFS和DFS的算法,这里我靠自己的理解还原其视频中的代码:
graph = {
'A': ['B', 'C', 'D'],
'B': ['A', 'C', 'E'],
'C': ['A', 'B', 'E'],
'D': ['A', 'E'],
'E': ['B', 'C', 'D', 'F'],
'F': ['E'],
}
def bfs(graph, s):
queue = [] # 设置一个空队列
queue.append(s) # 放入第一个节点
parent = {s: None} # 设置一个父级字典,并初始化
seen = set() # 设置一个空集合,用于记录走过的点
seen.add(s) # 加入列队的同时记录该点
while queue:
vertex = queue.pop(0) # 取出队列中的第一个元素
nodes = graph[vertex] # 获得该点的接通点
for node in nodes: # 遍历所有节点
if node not in seen: # 如果没有走过
queue.append(node) # 将节点放入队列
parent[node] = vertex # 记录该点的父节点
seen.add(node) # 加入的同时记录该点
# print(vertex)
return parent
def dfs(graph, s):
queue = [] # 设置一个空栈
queue.append(s) # 放入第一个节点
parent = {s: None} # 设置一个父级字典,并初始化
seen = set() # 设置一个空集合,用于记录走过的点
seen.add(s) # 加入列队的同时记录该点
while queue:
vertex = queue.pop() # 弹栈
nodes = graph[vertex] # 获得该点的接通点
for node in nodes: # 遍历所有节点
if node not in seen: # 如果没有走过
queue.append(node) # 将节点放入栈
parent[node] = vertex # 记录该点的父节点
seen.add(node) # 加入的同时记录该点
# print(vertex)
return parent
def show_path(graph, s, e, fs=bfs):
parent = fs(graph, s) # 遍历出所有路径
par = e # 设置终点
path = []
while par:
path.append(par) # 将终点加入路径列表
par = parent[par] # 求得终点的父节点,并且循环上一级
# 起点的父节点为空,所以会中断循环
path.reverse() # 倒置列表
result = '-->'.join(path) # 包装结果
print(result) # 打印结果
show_path(graph, 'A', 'F', fs=bfs)
show_path(graph, 'A', 'F', fs=dfs)
以上是BFS和DFS的代码,这里写一下自己的理解。
BFS 首先是讲起点节点放入队列,然后取出,取出的同时放入该节点的子节点,然后依次取出子节点,取出的同时放入子子节点…直至队列为空。
DFS也一样,不过不同的是放入的不是队列而知栈。
在BFS的基础上可以实现最短路径的查找:
import heapq
import math
graph = {
'A': {'B': 5, 'C': 1},
'B': {'A': 5, 'D': 1, 'C': 2},
'C': {'A': 1, 'B': 2, 'D': 4, 'E': 8},
'D': {'B': 1, 'C': 4, 'E': 3, 'F': 6},
'E': {'C': 8, 'D': 3},
'F': {'D': 6},
}
def init_distance(graph, s):
distance = {}
for node in graph:
if node == s: # 起点到起点的距离为零
distance[node] = 0
else: # 其他点到起点的距离为无穷
distance[node] = math.inf
return distance
def bfs(graph, s):
queue = [] # 建立一个队列
heapq.heappush(queue, (0, s)) # 放入队列
seen = set() # 建立一个空集,记录出现过的节点
parent = {s: None} # 建立一个字典,记录节点的父节点
distance = init_distance(graph, s) # 初始化到起点的距离
while queue:
pair = heapq.heappop(queue) # 取出一个顶点(距离,节点)
vertex = pair[1] # 顶点
dis_vertex = pair[0] # 顶点到起点的距离
seen.add(vertex)
# 取出的时候记录,队列里面存在同一节点不同距离的情况
# 所以唯有以取出的时候来判断
nodes = graph[vertex] # 取出节点{节点:距上一点距离}
for node, dis_parent in nodes.items():
if node not in seen:
# 该点到起点的距离为
# 其父节点到起点的距离+其到父节点的距离
dis_start = dis_vertex + dis_parent
if dis_start < distance[node]:
# 如果该点到起点的距离小于距离字典中的距离:
# 加入队列
heapq.heappush(queue, (dis_start, node))
# 记录距离
distance[node] = dis_start
# 记录父节点
parent[node] = vertex
return parent, distance
def show_path(graph, s, e):
parent, distance = bfs(graph, s)
par = e
path = []
while par:
path.append(par)#
par = parent[par]
path.reverse()
per_node = path[0]
result = per_node
for node in path[1:]:
result += '-' + str(graph[per_node][node]) + '->' + node
per_node = node
print(distance[e])
print(result)
show_path(graph, 'A', 'F')
这里使用了优先队列,将距离起点近的节点排在前面优先取出。