我正在修改单源最短路径算法,在视频中,老师提到 BFS / DFS 无法直接用于查找最短路径一个加权图(我想每个人都已经知道了)并且说要自己解决这个问题。
我想知道为什么它不能用于加权图的确切原因/解释。是由于边缘的重量还是其他因素?有人可以解释我,因为我感到有点困惑。
答案 0 :(得分:31)
考虑这样的图表:
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's algorithm代替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]
来自维基百科的这个GIF可以很好地显示发生的情况:
请注意,这看起来与BFS代码非常相似,唯一真正的区别是使用堆,按距离节点排序,而不是常规队列数据结构。
答案 1 :(得分:3)
虽然这是事实,但您可以在加权图表中使用BFS/DFS
,但图表中稍有变化,如果图表的权重为正整数,则可以使用{替换权重为n
的边缘{1}}边缘,权重为1,中间节点为n
。像这样:
n-1
将是:
A-(4)-B
并且不要在最终的BFS / DFS结果中考虑这些中间节点(如M1,M2,M3)。
这个算法的复杂度是O(V * M),M是我们边缘的最大权重,如果我们知道在我们的特定图A-(1)-M1-(1)-M2-(1)-M3-(1)-B
中这个算法可以考虑,但一般来说这个算法可能没有这么好的表现。
答案 2 :(得分:0)
老师提到BFS/DFS不能直接用于在加权图中寻找最短路径
对于初学者来说,DFS 不在考虑之列,并且根本不适用于最短路径。
其次,this answer 正确解释了为什么 BFS 在带循环的加权图上失败,并建议 Dijkstra's,用优先级队列替换队列,如果找到到达节点的新的、更短的路径,则允许放宽。
然而,并没有提到在加权有向无环图(加权DAG)上,Dijkstra's 是矫枉过正,可以在 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
来源: