Prim算法和Dijkstra算法之间的区别?


91

Dijkstra算法和Prim算法的确切区别是什么?我知道Prim's将给出MST,但Dijkstra生成的树也将是MST。那到底有什么区别呢?


3
是迪克斯特拉。“ ij”是荷兰语中的双元音(滑音),它是“ j”不是辅音的地方。

22
您有任何办法解决问题。
anuj pradhan

4
区分它们的区别,最好的办法是阅读一些源代码Dijkstra算法普里姆。主要区别在这里:对于Prim graph[u][v] < key[v],对于Dijkstra dist[u]+graph[u][v] < dist[v]。因此,从这两页的图形中可以看到,它们的不同主要是因为这两行代码。
JW.ZG

Answers:


146

Prim的算法为图构造一个最小的生成树,该树是连接图中所有节点的树,并且在连接所有节点的所有树中具有最小的总开销。但是,MST中任何两个节点之间的路径长度可能不是原始图中这两个节点之间的最短路径。例如,如果您要物理连接图中的节点以最小的总成本为其供电,则MST很有用。两个节点之间的路径长度可能不是最佳的也没关系,因为您所关心的只是它们已连接的事实。

Dijkstra的算法从某个源节点开始构建最短路径树。最短路径树是将图中的所有节点连接回源节点并具有使从源节点到图中任何其他节点的任何路径的长度最小化的树。例如,如果您想建立一个道路网络,使每个人都能尽可能高效地到达某个重要的重要地标,这将很有用。但是,不能保证最短路径树是最小生成树,并且最短路径树边缘的开销之和可能比MST的开销大得多。

另一个重要的差异涉及算法处理的图形类型。由于MST的概念假定图固有地是无向的,因此Prim的算法仅适用于无向图。(对于有向图,有一种称为“最小跨越树状化”的东西,但是找到它们的算法要复杂得多)。Dijkstra的算法在有向图上可以很好地工作,因为最短路径树确实可以有向。此外,Dijkstra的算法不一定在包含负边缘权重的图形中产生正确的解决方案,而Prim的算法可以处理此问题。

希望这可以帮助!


第一段没有意义,伙计。现在的问题是什么之间的区别Dijkstra算法和普里姆,其中Dijkstra算法是不是你说的话the length of a path between **any** two nodes,你应该只注重为什么,如果它不是最短的SRC节点和普里姆任何其他节点之间的距离不是最短的。我认为他一定是要向其他任何节点请求Prim中src节点。您为什么要谈论Prim中的任何两个节点?那当然不是最短的。
JW.ZG

1
我已经整理了有关Dijkstra算法的段落中的措辞,以阐明最短路径树只是源于源节点的最短路径的最小化器。我之所以设计这样的答案,是为了说明算法发现的内容,而不是它们如何在更高的层次上发挥作用,为什么会产生不同的结果以及为什么您不会期望它们相同。
templatetypedef

1
最简单的解释是在Prims中没有指定起始节点,但是在dijsktra中,您(需要一个起始节点)必须找到从给定节点到所有其他节点的最短路径。请参阅stackoverflow.com/a/51605961/6668734
Deepak Yadav,

1
@templatetypedef-当您说:“ [用Dijkstra] 建造一棵树的成本可能比MST的成本大得多。” 你能详细说明一下吗?
Amelio Vazquez-Reina

1
@ AmelioVazquez-Reina抱歉,这有点含糊。我的意思是,最短路径树的边缘上的权重之和可能比MST中边缘上的权重之和大得多。
templatetypedef

82

Dijkstra的算法不会创建MST,而是会找到最短的路径。

考虑这张图

       5     5
  s *-----*-----* t
     \         /
       -------
         9

最短的路径是9,而MST是10的不同“路径”。


2
好的,谢谢...您清除了一个需要注意的地方。直到现在我一直在考虑dijkstra生成的输出将是MST,但是您通过一个很好的例子消除了疑问。我可以清楚地看到我是否会使用“ kruskal”找到一个MST,那么我将获得与您提到的相同的路径。非常感谢
Anuj pradhan 2013年

8
更正确-- The shortest path is 9从s到t。由Dijkstra算法生成的图的权重从s开始为14(5 + 9)。
2013年

1
@Dukeling-嗯?Dijkstra的树/图的重量是没有意义的,这很重要....
dfb

4
非常简洁地说明!
Ram Narasimhan 2014年

1
@dfb:通常我们只运行Dijkstra的算法来获取特定顶点对之间的最短路径,但是实际上,您可以继续操作直到所有顶点都被访问为止,这将为您提供“最短路径树”,作为templatetypedef的答案解释。
j_random_hacker 2014年

63

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)
  • Dijkstra搜索最小路径长度,因此它关心边的积累。放松功能是alt = w(u,v) + u.key

在代码级别,另一个区别是API。Prim具有edges()返回MST边的方法,而Dijkstra具有的方法distanceTo(v)pathTo(v)分别返回从源到顶点v的距离以及从源到顶点v的路径,其中s是初始化Dijkstra所使用的顶点。
nethsix '16

1
推论,初始化普里姆任何任何源顶点,S返回相同的输出edges(),但是初始化的Dijkstra与不同的S将返回不同的输出distanceTo(v)pathTo(v)
nethsix '16

prims是否允许负重?如果是,那么这是另一个区别。我读到,您可以通过添加较大的正数来允许对prim负加权。每个值,使其全部为正。
阿希尔·爸爸

1
解决了我的困惑!完美答案!
Dhananjay Sarsonia

对于无向图,这里已处理的顶点必须忽略
AJ先生

53

Dijsktra的算法会找到从节点i到所有节点(您指定i)的最小距离。因此,作为回报,您可以获得距节点i的最小距离树。

Prims算法为您提供给定图的最小生成树。连接所有节点的树,而所有成本的总和是最小的。

因此,使用Dijkstra,您可以以最小的成本从选定的节点转到任何其他节点,而Prim's却无法做到这一点


最简单的解释是在Prims中没有指定起始节点,但是在dijsktra中,您(需要一个起始节点)必须找到从给定节点到所有其他节点的最短路径。请参阅stackoverflow.com/a/51605961/6668734
Deepak Yadav,

32

我看到的唯一区别是Prim的算法存储了最小成本边,而Dijkstra的算法存储了从源顶点到当前顶点的总成本。

Dijkstra为您提供了一种从源节点到目标节点的方法,以使成本最低。但是Prim的算法为您提供了最小的生成树,以便所有节点都已连接并且总成本最小。

简单来说:

因此,如果您想部署火车来连接多个城市,则可以使用Prim's算法。但是,如果您想从一个城市到另一个城市节省尽可能多的时间,则可以使用Dijkstra的算法。


24

两者都可以使用完全相同的通用算​​法来实现,如下所示:

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个半页长的论文中发表了这两种算法。


谢谢!出口模糊不清,即使什么也没发生,为什么仍要退出循环?
amirouche's

15

Dijkstra找到了它的起始节点和其他所有节点之间的最短路径。因此,作为回报,您可以获得距起始节点的最小距离树,即,您可以尽可能高效地到达所有其他节点。

Prims算法为您提供给定图的MST,即连接所有节点的树,而所有成本之和是最小的。

用一个真实的例子简短地讲一个故事:

  1. Dijkstra希望通过节省旅行时间和燃料来了解到达每个目的地的最短路径。
  2. 普里姆想知道如何有效地部署火车轨道系统,即节省材料成本。

10

直接来自Dijkstra算法的 Wikipedia文章:

Dijkstra算法基础的过程类似于Prim算法中使用的贪心过程。Prim的目的是找到连接图中所有节点的最小生成树。Dijkstra仅关注两个节点。Prim's不会评估从起始节点开始的路径的总权重,而只会评估单个路径。


5
“ Dijkstra只关心两个节点”。
tmyklebu 2014年

5

最近我被一个相同的问题困扰,我想我可能会分享我的理解...

我认为这两种算法(Dijkstra和Prim)之间的主要区别在于它们要解决的问题,即两个节点之间的最短路径和最小生成树(MST)。形式上是找到节点st之间的最短路径,而合理的条件是最多访问一次图的每个边。但是,它要求我们去参观的所有节点。后者(MST)是使我们(最多一次)访问所有节点,并且具有同样的理性要求,即最多访问每个边缘一次。

话虽这么说,Dijkstra允许我们“走捷径”,只要我能从st,而不必担心后果-一旦到达t,我就完成了!尽管MST中也有一条从st的路径,但是该s - t路径是在考虑所有其余节点的情况下创建的,因此,该路径可能比Dijstra算法找到的s - t路径更长。下面是一个包含3个节点的快速示例:

                                  2       2  
                          (s) o ----- o ----- o (t)     
                              |               |
                              -----------------
                                      3

假设每个顶部边缘的成本为2,而底部边缘的成本为3,则Dijktra会告诉我们采用底部路径,因为我们不在乎中间节点。另一方面,Prim会返回一个带有顶部2个边的MST,而不是底部边。

这种差异也体现在实现上的细微差别上:在Dijkstra的算法中,需要吸收每个新节点后,簿记步骤(针对每个节点)从s更新最短路径,而在Prim的算法中,没有这种需要。


3

基本算法之间的主要区别在于它们的边缘选择标准不同。通常,它们都使用优先级队列来选择下一个节点,但是有不同的标准来选择当前处理节点的相邻节点: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的计算是第二个不同点。


3

Dijkstra的算法是节点i和j之间的单源最短路径问题,而Prim的算法则是最小生成树问题。这些算法使用名为“贪心算法”的编程概念

如果您检查这些概念,请访问

  1. 贪婪算法讲义:http : //jeffe.cs.illinois.edu/teaching/algorithms/notes/07-greedy.pdf
  2. 最小生成树:http : //jeffe.cs.illinois.edu/teaching/algorithms/notes/20-mst.pdf
  3. 单一来源最短路径:http : //jeffe.cs.illinois.edu/teaching/algorithms/notes/21-sssp.pdf

2

Dijkstras算法仅用于查找最短路径。

最小生成树(Prim算法或Kruskal算法)中,您将获得具有最小边缘值的最小egdes。

例如:-考虑一种情况,您将不想创建一个庞大的网络,因为您将需要大量的电线,因此可以使用最小生成树(Prim算法或Kruskal算法) 完成这些电线计数(即,它将给您最少的电线数量,以最低的成本创建庞大的有线网络连接。

“ Dijkstras算法”将用于获得两个节点之间的最短路径,同时将任何节点彼此连接。


2

最简单的解释是在Prims中没有指定起始节点,但是在dijsktra中,您(需要一个起始节点)必须找到从给定节点到所有其他节点的最短路径。


0

@templatetypedef涵盖了MST和最短路径之间的差异。我已经在另一个So答案中讨论了算法的差异,方法是证明这两者都可以使用相同的通用算​​法来实现,该通用算法将一个参数作为输入:function f(u,v)。Prim和Dijkstra算法之间的区别仅在于f(u,v)您使用的算法。


0

在代码级别,另一个区别是API。

您使用源顶点s初始化Prim ,即Prim.new(s)s可以是任何顶点,而与s无关,最终结果与最小生成树(MST)的边缘相同。要获得MST边缘,我们调用方法edges()

使用源顶点s初始化Dijkstra ,即Dijkstra.new(s)您要获得到所有其他顶点的最短路径/距离。最终结果,即从s到所有其他顶点的最短路径/距离;根据s而不同。为了获得从s到任何顶点v的最短路径/距离,我们分别调用方法distanceTo(v)pathTo(v)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.