考虑一个类似于这样的图:
A---(3)-----B
| |
\-(1)-C--(1)/
从A到B的最短路径是经过C(总权重为2)。正常的BFS将直接从A到B,标记B已访问,并从A到C,标记C已访问。
在下一阶段,从C开始传播时,B已被标记为已访问,因此不会将从C到B的路径视为潜在的更短路径,BFS将告诉您从A到B的最短路径具有3的权重。
您可以使用 Dijkstra算法而不是BFS在加权图上找到最短路径。功能上,该算法与BFS非常相似,并且可以用类似于BFS的方式编写。唯一改变的是考虑节点的顺序。
例如,在上面的图中,从A开始,BFS将处理A->B,然后A->C,并停止,因为所有节点都已被访问。
另一方面,Dijkstra算法将按以下方式操作:
请注意,差异仅在于检查边的顺序。 BFS将在移动到其他节点之前考虑单个节点的所有边缘,而Dijkstra算法将始终考虑从已看到的节点集合中连接的边缘的最低权重未见过的边缘。听起来有点混乱,但伪代码非常简单:
create a heap or priority queue
place the starting node in the heap
dist[2...n] = {∞}
dist[1] = 0
while the heap contains items:
vertex v = top of heap
pop top of heap
for each vertex u connected to v:
if dist[u] > dist[v] + weight of v-->u:
dist[u] = dist[v] + weight of edge v-->u
place u on the heap with weight dist[u]
这张来自Wikipedia的GIF图很好地展示了发生的事情:
注意,这看起来非常类似于BFS代码,唯一的真正区别在于使用一个按节点距离排序的堆,而不是普通队列数据结构。
A-(4)-B
将会是:
A-(1)-M1-(1)-M2-(1)-M3-(1)-B
在最终的BFS/DFS结果中不要考虑这些中间节点(如M1、M2、M3)。
该算法的复杂度为O(V * M),其中M是边权的最大值,如果我们知道在特定的图中M<log V
,那么可以考虑使用该算法,但通常情况下该算法的性能可能不是很好。
O(|V|+|E|)
时间内找到单源最短路径。这种方法也适用于具有负权重边的DAG。以下是高级算法:distances = {V: infinity for V in vertices}
predecessors = {V: None for V in vertices}
for U in topological_sort(vertices):
for V in adjacencies(U):
if distances[V] > distances[U] + edge_weight(U, V): # relax the edge
distances[V] = distances[U] + edge_weight(U, V)
predecessors[V] = U