bfs 最短路径算法介绍
BFS(广度优先搜索,Breadth-First Search)是一种用于图遍历或搜索树或图的算法。尽管BFS本身不直接计算最短路径,但它可以用来实现某些情况下的最短路径算法,特别是在无权图(即所有边的权重都相等)中。
在无权图中,从源点到任何其他顶点的最短路径就是边数最少的路径。由于BFS总是首先访问离源点最近的节点(基于边的数量),因此它天然地可以用来找到源点到所有其他节点的最短路径(基于边的数量)。
BFS算法的基本思想
初始化:选择一个源点,并将其标记为已访问。创建一个队列,并将源点加入队列。
循环:当队列不为空时,进行循环。从队列中移除一个节点,并检查它的所有邻接点。对于每一个邻接点:
如果邻接点未被访问过,则将其标记为已访问,并将其加入队列。
(可选)记录到达每个邻接点的路径或步数。
结束:当队列为空时,算法结束。所有从源点可达的节点都已被访问,且对于无权图,已经找到了从源点到这些节点的最短路径(基于边的数量)。
BFS实现最短路径的示例
考虑一个简单的无权图,使用邻接表表示。我们需要找到从节点S到所有其他节点的最短路径(基于边的数量)。
from collections import deque
# 图的邻接表表示
graph = {
'A': ['B', 'C'],
'B': ['A', 'D', 'E'],
'C': ['A', 'F'],
'D': ['B'],
'E': ['B', 'F'],
'F': ['C', 'E'],
}
def bfs(graph, start):
visited = set() # 访问过的节点
queue = deque([(start, [start])]) # 队列中的元素是(节点, 从起点到该节点的路径)
while queue:
vertex, path = queue.popleft()
if vertex not in visited:
visited.add(vertex)
print(f"从 {start} 到 {vertex} 的最短路径是: {path}")
for neighbour in graph[vertex]:
if neighbour not in visited:
queue.append((neighbour, path + [neighbour]))
bfs(graph, 'A')
注意:上述代码假设了所有边的权重都是1,且没有循环边(即不是有向图且没有环)。对于有权图,BFS无法直接用来找到最短路径(基于权重),这时应该使用Dijkstra算法(对于非负权重)或Bellman-Ford算法(对于可能包含负权重的图)。
bfs 最短路径算法python实现样例
下面是使用Python实现BFS最短路径算法的示例代码:
from queue import Queue
def bfs_shortest_path(graph, start, end):
# 创建一个队列并将起始节点入队
queue = Queue()
queue.put([start])
while not queue.empty():
# 从队列中取出一条路径
path = queue.get()
# 获取路径的最后一个节点
node = path[-1]
# 如果当前节点是目标节点,则返回路径
if node == end:
return path
# 遍历当前节点的相邻节点
for adjacent_node in graph.get(node, []):
# 如果相邻节点未在路径中,则将路径拓展并入队
if adjacent_node not in path:
new_path = list(path)
new_path.append(adjacent_node)
queue.put(new_path)
# 如果没有找到路径,则返回空列表
return []
# 测试示例
graph = {
'A': ['B', 'C'],
'B': ['A', 'D', 'E'],
'C': ['A', 'F'],
'D': ['B'],
'E': ['B', 'F'],
'F': ['C', 'E']
}
start_node = 'A'
end_node = 'F'
shortest_path = bfs_shortest_path(graph, start_node, end_node)
print(shortest_path)
在这个示例中,我们首先创建一个队列,然后将起始节点入队。然后我们进入一个循环,直到队列为空。在每一次循环中,我们从队列中取出一条路径,并获取路径的最后一个节点。如果最后一个节点是目标节点,则我们已经找到了最短路径,可以返回它。
否则,我们遍历最后一个节点的相邻节点,并将相邻节点拓展到路径中,并入队。我们还需要检查路径中是否已经包含了相邻节点,以避免形成环路。如果我们在循环结束后都没有找到目标节点,则返回一个空列表。