Dijkstra算法和Prim算法的确切区别是什么?我知道Prim算法会给出MST,但由Dijkstra生成的树也将是MST。那么确切的区别是什么?
Dijkstra算法和Prim算法的确切区别是什么?我知道Prim算法会给出MST,但由Dijkstra生成的树也将是MST。那么确切的区别是什么?
Prim算法构建一个最小生成树,该树连接图中的所有节点,并且在所有连接所有节点的树中具有最小的总成本。然而,在MST中任意两个节点之间的路径长度可能不是原始图中这两个节点的最短路径。例如,如果你想以最小的总成本为节点提供电力,那么MST非常有用。由于你只关心它们是否连接,因此两个节点之间的路径长度是否最优并不重要。
Dijkstra算法从某个源节点开始构建最短路径树。最短路径树是一棵树,将图中所有节点连接回源节点,并具有这样的属性:源节点到图中任何其他节点的路径长度都被最小化。例如,如果您想建立一个道路网络,使每个人到达某个重要地标的效率尽可能高,则此方法很有用。但是,最短路径树不能保证是最小生成树,最短路径树边上的成本之和可能比MST的成本大得多。另一个重要的区别是算法适用于哪些类型的图形。Prim算法仅适用于无向图,因为MST的概念假定图形本质上是无向的。(对于有向图,有一种称为“最小生成树”的东西,但寻找它们的算法要复杂得多)。Dijkstra算法在有向图上也可以正常工作,因为最短路径树确实可以是有向的。此外,在包含负边缘权重的图形中,Dijkstra算法 不一定能产生正确的解决方案,而Prim算法可以处理这种情况。
迪杰斯特拉算法并不会创建最小生成树,它是用来寻找最短路径的。
考虑这个图:
5 5
s *-----*-----* t
\ /
-------
9
普里姆算法和迪杰斯特拉算法几乎相同,除了 "松弛函数"。
普里姆算法:
MST-PRIM (G, w, r) {
for each key ∈ G.V
u.key = ∞
u.parent = NIL
r.key = 0
Q = G.V
while (Q ≠ ø)
u = Extract-Min(Q)
for each v ∈ G.Adj[u]
if (v ∈ Q)
alt = w(u,v) <== relax function, Pay attention here
if alt < v.key
v.parent = u
v.key = alt
}
Dijkstra:
Dijkstra (G, w, r) {
for each key ∈ G.V
u.key = ∞
u.parent = NIL
r.key = 0
Q = G.V
while (Q ≠ ø)
u = Extract-Min(Q)
for each v ∈ G.Adj[u]
if (v ∈ Q)
alt = w(u,v) + u.key <== relax function, Pay attention here
if alt < v.key
v.parent = u
v.key = alt
}
唯一的区别被箭头指出,就是松弛函数。
alt = w(u,v)
alt = w(u,v) + u.key
edges()
输出,但是使用不同的s初始化Dijkstra算法将返回不同的distanceTo(v)
和pathTo(v)
输出。 - nethsix迪杰斯特拉算法可以找到从节点i到所有节点的最小距离(您指定i)。因此,您可以得到从节点i开始的最小距离树。
普里姆算法可以为给定图形获取最小生成树。这是一棵连接所有节点的树,而所有成本的总和是可能的最小值。
因此,使用Dijkstra你可以以最小的成本从选定的节点到任何其他节点,但使用Prim's算法无法做到这一点。
两者都可以使用完全相同的通用算法来实现,具体如下:
Inputs:
G: Graph
s: Starting vertex (any for Prim, source for Dijkstra)
f: a function that takes vertices u and v, returns a number
Generic(G, s, f)
Q = Enqueue all V with key = infinity, parent = null
s.key = 0
While Q is not empty
u = dequeue Q
For each v in adj(u)
if v is in Q and v.key > f(u,v)
v.key = f(u,v)
v.parent = u
对于Prim算法,传递f = w(u, v)
,对于Dijkstra算法,传递f = u.key + w(u, v)
。
另一个有趣的事情是,上面的通用算法也可以实现广度优先搜索(BFS),尽管这将是一种过度使用,因为昂贵的优先队列并不是真正必要的。要将上述通用算法转换为BFS,请传递f = u.key + 1
,这与强制所有权重为1相同(即BFS给出从点A到点B所需的最小边数)。
直觉
以下是思考上述通用算法的一个好方法:我们从两个桶A和B开始。最初,将所有顶点放入B中,使桶A为空。然后我们将一个顶点从B移到A。现在看看从A中的顶点到B中的顶点的所有边缘。我们使用某些标准选择其中一个边缘,并将相应的顶点从B移动到A。重复此过程直到B为空。
一种实现这个想法的暴力方法是维护一个优先级队列,其中包含跨越到B的A中顶点的边。显然,如果图不稀疏,这将会很麻烦。所以问题是,我们是否可以维护顶点的优先级队列来代替?实际上我们可以,因为我们的最终决策是从B中选择哪个顶点。与此无关的是,1956年,Dijkstra需要编写一个程序来展示他所在机构开发的新计算机的能力。他认为让计算机找到荷兰两个城市之间的连接非常酷。他设计了这个算法只用了20分钟。他创建了一个由64个城市组成的图表,并为这台1956年的计算机编写了代码(因为他的计算机只有6位数)。然而,他没有公开发布他的算法,因为当时还没有计算机科学期刊,他认为这可能并不是非常重要的事情。隔年,他了解到将新计算机的终端连接起来以使电线长度最小化的问题。他思考了这个问题,并重新发现了Jarník/Prim算法,这个算法再次使用了他一年前发现的最短路径算法相同的技术。他 提到 他的两个算法都是在没有使用纸笔的情况下设计的。1959年,他在一篇只有两页半的 论文 中同时发表了这两个算法。
迪杰斯特拉算法的过程类似于Prim算法中使用的贪婪过程。Prim的目的是找到连接图中所有节点的最小生成树;Dijkstra只关心两个节点。Prim不评估从起始节点到终点的总路径权重,而只评估单个路径的权重。
List parent(listLen=n, valueOfEachElement=-1); parent[neighborVertex] = currentVertex;
parent
是下一个节点(neighborVertex)来自于哪里的信息;请记住,图是树结构。 - undefined最近我一直在困扰同一个问题,我认为我可以分享一下我的理解...
我认为这两个算法(Dijkstra和Prim)的关键差异在于它们所设计解决的问题,即两个节点之间的最短路径和最小生成树(MST)。前者是要找到从节点s到t的最短路径,并且合理的要求是每条边最多只访问一次,但不需要访问所有节点。后者则是要访问所有节点(至多一次),并且有同样的合理要求,即每条边最多只访问一次。
也就是说,Dijkstra允许我们采用“捷径”,只要我能够从s到达t,而不必担心其后果 - 一旦到达t,就完成了!虽然在MST中也有从s到t的路径,但这个s-t路径是考虑了所有其他节点的情况下创建的,因此,这条路径可能比Dijstra算法找到的s-t路径更长。以下是一个具有3个节点的快速例子:
2 2
(s) o ----- o ----- o (t)
| |
-----------------
3
graph[u][v] < key[v]
,而对于 Dijkstra 算法,条件为dist[u]+graph[u][v] < dist[v]
。因此,从这两个页面中的图表可以看出,它们之间的主要区别在于这两行代码。 - JW.ZG