Prim 算法和 Dijkstra 算法的区别?

Posted

技术标签:

【中文标题】Prim 算法和 Dijkstra 算法的区别?【英文标题】:Difference between Prim's and Dijkstra's algorithms? 【发布时间】:2012-12-18 03:10:31 【问题描述】:

Dijkstra 和 Prim 算法之间的确切区别是什么?我知道 Prim 会给出一个 MST,但 Dijkstra 生成的树也将是一个 MST。那么具体有什么区别呢?

【问题讨论】:

区分它们的最佳方法是阅读一些源代码,Dijkstra 和Prim。主要区别在于:Prim graph[u][v] < key[v] 和 Dijkstra dist[u]+graph[u][v] < dist[v]。所以从这两页的图表可以看出,它们的不同主要是因为这两行代码。 What is the difference between Dijkstra and Prim's algorithm?的可能重复 【参考方案1】:

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

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

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

【讨论】:

【参考方案2】:

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

Prims 算法为您提供给定图的最小生成树。一棵连接所有节点的树,同时所有成本的总和尽可能小。

因此,使用 Dijkstra 您可以以最低的成本从选定的节点转到任何其他节点,而 Prim 做不到这一点

【讨论】:

【参考方案3】:

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

Dijkstra 为您提供了一种从源节点到目标节点的方法,从而使成本最低。然而,Prim 的算法为您提供了一个最小生成树,这样所有节点都连接起来并且总成本最小。

简单来说:

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

【讨论】:

【参考方案4】:

直接来自Dijkstra's Algorithm's***文章:

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

【讨论】:

“Dijkstra 只关心两个节点”是废话。【参考方案5】:

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

考虑这个图表

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

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

【讨论】:

好的,谢谢...您清除了一个很好的注意点。到目前为止,我一直在考虑 dijkstra 生成的输出将是一个 MST,但你用一个很好的例子消除了疑问。我可以清楚地看到我是否会使用 say 'kruskal' 找到一个 MST,那么我将得到与你提到的相同的路径.非常感谢 更正确 - The shortest path is 9 ... 从 s 到 t。 Dijkstra算法生成的图的权重,从s开始,为14(5+9)。 @Dukeling - 嗯? Dijkstra 中的树/图的权重是没有意义的,这就是重点.... 插图非常简洁! @dfb: 通常我们只运行 Dijkstra 的算法来获得一对特定顶点之间的最短路径,但实际上你可以继续下去,直到所有顶点都被访问过,这会给你一个“最短路径树”,正如 templatetypedef 的回答所解释的那样。【参考方案6】:

Prim 的算法为图构造了一个minimum spanning tree,这是一棵连接图中所有节点的树,在连接所有节点的所有树中总成本最低。但是,MST 中任意两个节点之间的路径长度可能不是原始图中这两个节点之间的最短路径。 MST 很有用,例如,如果您想以物理方式连接图表中的节点,以最低的总成本为它们提供电力。两个节点之间的路径长度可能不是最优的并不重要,因为您只关心它们是连接的事实。

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

另一个重要的区别是算法处理的图表类型。 Prim 的算法仅适用于无向图,因为 MST 的概念假定图本质上是无向的。 (有向图有一种叫做“最小跨越树状结构”的东西,但是找到它们的算法要复杂得多)。 Dijkstra 的算法可以在有向图上正常工作,因为最短路径树确实可以被定向。另外,Dijkstra 的算法does not necessarily yield the correct solution in graphs containing negative edge weights,而 Prim 的算法可以处理这个问题。

希望这会有所帮助!

【讨论】:

第一段没有意义,伙计。问题是 Dijkstra 和 Prim 之间有什么区别,其中 Dijkstra 与您所说的 the length of a path between **any** two nodes 无关,您应该只关注为什么 src 节点和任何其他节点之间的距离如果 Prim 中的节点不是最短的,则它不是最短的。我认为他一定是在询问 Prim 中的 src 节点到任何其他节点。您为什么要谈论 Prim 中的任意两个节点?这当然不是最短的。 我已经清理了段落中关于 Dijkstra 算法的措辞,以澄清最短路径树只是源自源节点的最短路径的最小化。我之所以这样安排我的答案,是为了说明算法发现了什么,而不是它们是如何工作的,以便在更高的层次上展示它们为什么会产生不同的结果以及为什么你没想到他们会是一样的。 最简单的解释是在 Prims 你不指定起始节点,但在 dijsktra 你(需要有一个起始节点)有找到从给定节点到所有其他节点的最短路径。见***.com/a/51605961/6668734 @templatetypedef - 当你说:"和构建这样一棵树 [with Dijkstra] 的成本可能比大得多 MST 的费用。” 你能详细说明一下吗? @AmelioVazquez-Reina 抱歉,这有点模棱两可。我的意思是,最短路径树边缘的权重之和可能远大于 MST 中边缘的权重之和。【参考方案7】:

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

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

用一个真实的例子来做一个简短的故事:

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

【讨论】:

【参考方案8】:

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

【讨论】:

【参考方案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 (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

箭头所指的唯一区别是relax函数。

搜索最小生成树的 Prim 只关心覆盖所有顶点的总边中的最小值。放松功能是alt = w(u,v) Dijkstra,它搜索最小路径长度,因此它关心边缘累积。放松功能是alt = w(u,v) + u.key

【讨论】:

在代码级别,另一个区别是API。 Prim 有方法 edges() 来返回 MST 边,而 Dijkstra 有 distanceTo(v)pathTo(v),分别返回从源到顶点 v 的距离和从源到顶点 v 的路径,其中 s 是你初始化 Dijkstra 的顶点。 推论,用任何任何源顶点初始化 Prim,s 为 edges() 返回相同的输出,但用不同的 s 初始化 Dijkstra 将为 distanceTo(v)pathTo(v) 返回不同的输出。 prims 允许负重吗?如果是,那么这是另一个区别。我读到你可以通过添加大的正数来允许对 prim 的负权重。对每个值,使其全部为正。 解决了我的困惑!完美答案!! 难以置信的答案!有一种直觉认为这两个算法非常相似,但我无法确切地指出如何 - 感谢这个美丽的答案!【参考方案10】:

@templatetypedef 涵盖了 MST 和最短路径之间的区别。我已经介绍了another So answer 中的算法差异,证明两者都可以使用相同的通用算​​法来实现,该算法需要一个参数作为输入:函数f(u,v)。 Prim 和 Dijkstra 算法的区别只是你使用的是f(u,v)

【讨论】:

【参考方案11】:

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

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)

另一个有趣的事情是,上面的 Generic 也可以实现广度优先搜索(BFS),虽然它会有点过分,因为昂贵的优先级队列并不是真正需要的。要将上述通用算法转入 BFS,请传递 f = u.key + 1,这与强制所有权重为 1 相同(即 BFS 给出从 A 点遍历到 B 点所需的最小边数)。

直觉

这是考虑上述通用算法的一种好方法:我们从两个桶 A 和 B 开始。最初,将所有顶点放在 B 中,因此桶 A 是空的。然后我们将一个顶点从 B 移动到 A。现在查看从 A 中的顶点交叉到 B 中的顶点的所有边。我们使用这些交叉边中的一些标准选择一条边,并将相应的顶点从 B 移动到A. 重复这个过程,直到 B 为空。

实现这个想法的蛮力方法是为 A 中的顶点维护一个优先级队列,该队列跨越到 B。显然,如果图形不是稀疏的,这将是麻烦的。所以问题是我们可以改为维护顶点的优先级队列吗?事实上,我们可以最终决定从 B 中选择哪个顶点。

历史背景

有趣的是,这两种算法背后技术的通用版本在概念上与 1930 年一样古老,即使当时还没有电子计算机。

故事从 Otakar Borůvka 开始,他需要一个算法,让一位家庭朋友试图弄清楚如何用最低成本的电线连接摩拉维亚国家(现为捷克***的一部分)的城市。他于 1926 年在与数学相关的期刊上发表了他的算法,因为当时计算机科学还不存在。这引起了 Vojtěch Jarník 的注意,他想到了对 Borůvka 算法的改进并于 1930 年将其发表。事实上,他发现了与我们现在所知的 Prim 算法相同的算法,并在 1957 年重新发现了它。

独立于所有这些,1956 年 Dijkstra 需要编写一个程序来展示他的研究所开发的新计算机的功能。他认为让计算机找到在荷兰两个城市之间旅行的连接会很酷。他在 20 分钟内设计了算法。他创建了一个包含 64 个城市的图表,并进行了一些简化(因为他的计算机是 6 位的),并为这台 1956 年的计算机编写了代码。但是他没有发表他的算法,因为主要是没有计算机科学期刊,他认为这可能不是很重要。第二年,他了解到连接新计算机终端的问题,以使电线的长度最小化。他思考了这个问题并重新发现了 Jarník/Prim 的算法,该算法再次使用了与他一年前发现的最短路径算法相同的技术。他mentioned 他的两种算法都是在没有使用笔或纸的情况下设计的。 1959 年,他将这两种算法都发表在了一个只有 2 页半的paper 中。

【讨论】:

谢谢!退出是模糊的,为什么即使什么都没发生它也会退出循环?【参考方案12】:

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

您使用源顶点 s 初始化 Prim,即 Prim.new(s)s 可以是任意顶点,不管s,最终的结果,即最小生成树(MST)的边都是一样的。要获取 MST 边,我们调用方法edges()

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

【讨论】:

【参考方案13】:

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

我认为这两种算法(Dijkstra 和 Prim)之间的关键区别在于它们旨在解决的问题,即两个节点之间的最短路径和最小生成树 (MST)。形式是找到节点 st 之间的最短路径,合理的要求是最多访问图的每条边一次。但是,它确实 NOT 要求我们访问所有节点。后者(MST)是让我们访问 ALL 节点(最多一次),并且同样的理性要求也最多访问每条边一次。

话虽如此,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 的算法中,没有这样的需要。

【讨论】:

【参考方案14】:

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

Minimum Spanning tree(Prim's or Kruskal's algorithm)中,您可以获得最小边值的最小边值。

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

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

【讨论】:

【参考方案15】:

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

【讨论】:

【参考方案16】:

这就是我的想法:想想算法接下来需要哪个顶点:

Prim 的算法采用下一个最接近树的顶点,即最接近树上任意位置的某个顶点

Dijkstra 的算法采用下一个离源最近的顶点。

来源:R. Sedgewick 关于 Dijkstra 算法的讲座,算法,第二部分:https://coursera.org/share/a551af98e24292b6445c82a2a5f16b18

【讨论】:

以上是关于Prim 算法和 Dijkstra 算法的区别?的主要内容,如果未能解决你的问题,请参考以下文章

Dijkstra算法与Prim算法的异同

数据结构与算法系列研究七——图prim算法dijkstra算法

图的Prim,Kruskal,Dijkstra,Floyd算法

Prim算法

kruskal算法

最小生成树(Prim,Kruskal)--最短路径(Dijkstra,Floyd)算法详解