Dijkstra算法和Prim算法的确切区别是什么?我知道Prim's将给出MST,但Dijkstra生成的树也将是MST。那到底有什么区别呢?
graph[u][v] < key[v]
,对于Dijkstra dist[u]+graph[u][v] < dist[v]
。因此,从这两页的图形中可以看到,它们的不同主要是因为这两行代码。
Dijkstra算法和Prim算法的确切区别是什么?我知道Prim's将给出MST,但Dijkstra生成的树也将是MST。那到底有什么区别呢?
graph[u][v] < key[v]
,对于Dijkstra dist[u]+graph[u][v] < dist[v]
。因此,从这两页的图形中可以看到,它们的不同主要是因为这两行代码。
Answers:
Prim的算法为图构造一个最小的生成树,该树是连接图中所有节点的树,并且在连接所有节点的所有树中具有最小的总开销。但是,MST中任何两个节点之间的路径长度可能不是原始图中这两个节点之间的最短路径。例如,如果您要物理连接图中的节点以最小的总成本为其供电,则MST很有用。两个节点之间的路径长度可能不是最佳的也没关系,因为您所关心的只是它们已连接的事实。
Dijkstra的算法从某个源节点开始构建最短路径树。最短路径树是将图中的所有节点连接回源节点并具有使从源节点到图中任何其他节点的任何路径的长度最小化的树。例如,如果您想建立一个道路网络,使每个人都能尽可能高效地到达某个重要的重要地标,这将很有用。但是,不能保证最短路径树是最小生成树,并且最短路径树边缘的开销之和可能比MST的开销大得多。
另一个重要的差异涉及算法处理的图形类型。由于MST的概念假定图固有地是无向的,因此Prim的算法仅适用于无向图。(对于有向图,有一种称为“最小跨越树状化”的东西,但是找到它们的算法要复杂得多)。Dijkstra的算法在有向图上可以很好地工作,因为最短路径树确实可以有向。此外,Dijkstra的算法不一定在包含负边缘权重的图形中产生正确的解决方案,而Prim的算法可以处理此问题。
希望这可以帮助!
the length of a path between **any** two nodes
,你应该只注重为什么,如果它不是最短的SRC节点和普里姆任何其他节点之间的距离不是最短的。我认为他一定是要向其他任何节点请求Prim中的src节点。您为什么要谈论Prim中的任何两个节点?那当然不是最短的。
Dijkstra的算法不会创建MST,而是会找到最短的路径。
考虑这张图
5 5
s *-----*-----* t
\ /
-------
9
最短的路径是9,而MST是10的不同“路径”。
The shortest path is 9
从s到t。由Dijkstra算法生成的图的权重从s开始为14(5 + 9)。
Prim和Dijkstra算法几乎相同,除了“松弛函数”。
首要:
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()
返回MST边的方法,而Dijkstra具有的方法distanceTo(v)
,pathTo(v)
分别返回从源到顶点v的距离以及从源到顶点v的路径,其中s是初始化Dijkstra所使用的顶点。
edges()
,但是初始化的Dijkstra与不同的S将返回不同的输出distanceTo(v)
,pathTo(v)
。
Dijsktra的算法会找到从节点i到所有节点(您指定i)的最小距离。因此,作为回报,您可以获得距节点i的最小距离树。
Prims算法为您提供给定图的最小生成树。连接所有节点的树,而所有成本的总和是最小的。
因此,使用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 pass 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。 A.重复此过程,直到B为空。
实施此想法的一种蛮力方法是为A中与B交叉的顶点的边缘保留优先队列。显然,如果图形不稀疏,这将很麻烦。那么问题是我们可以代替维护顶点的优先级队列吗?事实上,我们最终可以决定从B选择哪个顶点。
历史背景
有趣的是,即使在没有电子计算机的情况下,这两种算法背后的技术的通用版本在概念上也早于1930年。
故事始于OtakarBorůvka,他需要一个家庭朋友的算法,试图弄清楚如何用最低成本的电线连接摩拉维亚(现在是捷克共和国的一部分)国家的城市。他于1926年在与数学相关的杂志上发表了他的算法,因为当时还不存在计算机科学。这引起了VojtěchJarník的注意,他想到了Borůvka算法的改进并于1930年发布。实际上,他发现了现在与Prim的算法相同的算法,后者在1957年重新发现了它。
独立于所有这些,Dijkstra在1956年需要编写一个程序来演示其研究所开发的新计算机的功能。他认为,让计算机找到在荷兰两个城市之间旅行的联系会很酷。他在20分钟内设计了算法。他创建了一个包含64个城市的图形的简化图(因为他的计算机是6位的),并为此1956年的计算机编写了代码。但是,他没有发布算法,因为主要是没有计算机科学期刊,他认为这可能不是很重要。第二年,他了解了有关连接新计算机终端的问题,以使电线长度最小化。他考虑了这个问题,并重新发现了Jarník/ Prim' 的算法再次使用了与一年前发现的最短路径算法相同的技术。他提到他的两种算法都是在不使用笔或纸的情况下设计的。1959年,他在只有2个半页长的论文中发表了这两种算法。
直接来自Dijkstra算法的 Wikipedia文章:
Dijkstra算法基础的过程类似于Prim算法中使用的贪心过程。Prim的目的是找到连接图中所有节点的最小生成树。Dijkstra仅关注两个节点。Prim's不会评估从起始节点开始的路径的总权重,而只会评估单个路径。
最近我被一个相同的问题困扰,我想我可能会分享我的理解...
我认为这两种算法(Dijkstra和Prim)之间的主要区别在于它们要解决的问题,即两个节点之间的最短路径和最小生成树(MST)。形式上是找到节点s和t之间的最短路径,而合理的条件是最多访问一次图的每个边。但是,它不要求我们去参观的所有节点。后者(MST)是使我们(最多一次)访问所有节点,并且具有同样的理性要求,即最多访问每个边缘一次。
话虽这么说,Dijkstra允许我们“走捷径”,只要我能从s到t,而不必担心后果-一旦到达t,我就完成了!尽管MST中也有一条从s到t的路径,但是该s - t路径是在考虑所有其余节点的情况下创建的,因此,该路径可能比Dijstra算法找到的s - t路径更长。下面是一个包含3个节点的快速示例:
2 2
(s) o ----- o ----- o (t)
| |
-----------------
3
假设每个顶部边缘的成本为2,而底部边缘的成本为3,则Dijktra会告诉我们采用底部路径,因为我们不在乎中间节点。另一方面,Prim会返回一个带有顶部2个边的MST,而不是底部边。
这种差异也体现在实现上的细微差别上:在Dijkstra的算法中,需要吸收每个新节点后,簿记步骤(针对每个节点)从s更新最短路径,而在Prim的算法中,没有这种需要。
基本算法之间的主要区别在于它们的边缘选择标准不同。通常,它们都使用优先级队列来选择下一个节点,但是有不同的标准来选择当前处理节点的相邻节点:Prim的算法要求下一个相邻节点也必须保留在队列中,而Dijkstra的算法则不:
def dijkstra(g, s):
q <- make_priority_queue(VERTEX.distance)
for each vertex v in g.vertex:
v.distance <- infinite
v.predecessor ~> nil
q.add(v)
s.distance <- 0
while not q.is_empty:
u <- q.extract_min()
for each adjacent vertex v of u:
...
def prim(g, s):
q <- make_priority_queue(VERTEX.distance)
for each vertex v in g.vertex:
v.distance <- infinite
v.predecessor ~> nil
q.add(v)
s.distance <- 0
while not q.is_empty:
u <- q.extract_min()
for each adjacent vertex v of u:
if v in q and weight(u, v) < v.distance:// <-------selection--------
...
vertex.distance的计算是第二个不同点。
Dijkstra的算法是节点i和j之间的单源最短路径问题,而Prim的算法则是最小生成树问题。这些算法使用名为“贪心算法”的编程概念
如果您检查这些概念,请访问
最简单的解释是在Prims中没有指定起始节点,但是在dijsktra中,您(需要一个起始节点)必须找到从给定节点到所有其他节点的最短路径。
@templatetypedef涵盖了MST和最短路径之间的差异。我已经在另一个So答案中讨论了算法的差异,方法是证明这两者都可以使用相同的通用算法来实现,该通用算法将一个参数作为输入:function f(u,v)
。Prim和Dijkstra算法之间的区别仅在于f(u,v)
您使用的算法。