图算法(1):Dijkstra's algorithm

  Dijkstra’s algorithm使用了广度优先搜索解决非负权图的单源最短路径问题,算法最终得到一个最短路径树。该算法常用于路由算法或者作为其他图算法的一个子模块。举例来说,如果图中的顶点表示城市,而边上的权重表示著城市间开车行经的距离,该算法可以用来找到两个城市之间的最短路径。

    

  其基本原理是:每次新扩展一个距离最短的点,更新与其相邻的点的距离。当所有边权都为正时,由于不会存在一个距离更短的没扩展过的点,所以这个点的距离永远不会再被改变,因而保证了算法的正确性。不过根据这个原理,用Dijkstra求最短路的图不能有负权边,因为扩展到负权边的时候会产生更短的距离,有可能就破坏了已经更新的点距离不会改变的性质。

  Dijkstra 算法的基础操作是边的拓展:如果存在一条从 u 到 v 的边,那么从 s 到 v 的最短路径可以通过将边(u, v)添加到尾部来拓展一条从 s 到 v 的路径。这条路径的长度是 d[u] + w(u, v)。如果这个值比目前已知的 d[v] 的值要小,我们可以用新值来替代当前 d[v] 中的值。拓展边的操作一直运行到所有的 d[v] 都代表从 s 到 v 最短路径的花费。这个算法经过组织因而当 d[u] 达到它最终的值的时候每条边(u, v)都只被拓展一次。

  不采用最小优先级队列,时间复杂度是O(|V|^2)(其中|V|为图的顶点个数)。通过斐波那契堆实现的迪科斯彻算法时间复杂度是O(|E|+|V|\log|V|) (其中|E|是边数) (Fredman & Tarjan 1984)。对于不含负权的有向图,这是目前已知的最快的单源最短路径算法。

伪代码:

在下面的算法中,u := Extract_Min(Q) 在顶点集合 Q 中搜索有最小的 d[u] 值的顶点 u。这个顶点被从集合 Q 中删除并返回给用户。

function Dijkstra(G, w, s)
    for each vertex v in V[G]                        // 初始化
        d[v] := infinity
        previous[v] := undefined                         // 各点的已知最短路径上的前趋都未知
    d[s] := 0                                              // 因为出发点到出发点间不需移动任何距离,所以可以直接将s到s的最小距离设为0
    S := empty set
    Q := set of all vertices
    while Q is not an empty set                      // Dijkstra演算法主体
        u := Extract_Min(Q)
        S.append(u)
        for each edge outgoing from u as (u,v)
            if d[v] > d[u] + w(u,v) // 拓展边(u,v)。w(u,v)为从u到v的路径长度。
                d[v] := d[u] + w(u,v)               // 更新路径长度到更小的那个和值。
                previous[v] := u                    // 记录前驱结点

  如果我们只对在 s 和 t 之间查找一条最短路径的话,我们可以添加条件如果满足 u = t 的话终止程序。

  通过推导可知,为了记录最佳路径的轨迹,我们只需记录该路径上每个点的前趋,即可通过迭代来回溯出 s 到 t 的最短路径(当然,使用后继节点来存储亦可。但那需要修改代码):

s := empty sequence
u := t
while defined u
    insert u to the beginning of S
    u := previous[u]      //previous数组即为上文中的p

  现在序列 S 就是从 s 到 t 的最短路径的顶点集。

#include <limits>
const int V = 9;
const int INF = numeric_limits<int>::max();

void printSolution(int d[])
{
   cout<<"Vertex Distance from Source\n";
   for (int i = 0; i < V; i++)
      cout<<i<<"\t\t"<<d[i]<<endl;
}

void dijkstra(int graph[V][V], int src)
{
    int  d[V];
    bool include[V];

    //初始化
    for(int i = 0; i < V; i++)
    {
        d[i] = INF;
        include[i] = false;
    }
    d[src] = 0;

    for(int i = 0; i < V; i++)
    {
        //选取当前最小距离点
        int min_dist = INF;
        int min_index = 0;
        for(int i = 0; i < V; i++)
        {
            if(d[i] < min_dist && include[i] == false)
            {
                min_dist = d[i];
                min_index = i;
            }
        }

        //标记所选最小距离点,表示已加入结果集
        include[min_index] = true;

        //更新所选点相邻点的距离
        for(int i = 0; i < V; i++)
            if(include[i] == false && graph[min_index][i] && d[min_index] + graph[min_index][i] < d[i])
                d[i] = d[min_index] + graph[min_index][i];
    }

    printSolution(d);
}

int _tmain(int argc, _TCHAR* argv[])
{

    int graph[V][V] = {{0, 4, 0, 0, 0, 0, 0, 8, 0},
                      {4, 0, 8, 0, 0, 0, 0, 11, 0},
                      {0, 8, 0, 7, 0, 4, 0, 0, 2},
                      {0, 0, 7, 0, 9, 14, 0, 0, 0},
                      {0, 0, 0, 9, 0, 10, 0, 0, 0},
                      {0, 0, 4, 0, 10, 0, 2, 0, 0},
                      {0, 0, 0, 14, 0, 2, 0, 1, 6},
                      {8, 11, 0, 0, 0, 0, 1, 0, 7},
                      {0, 0, 2, 0, 0, 0, 6, 7, 0}
                     };

    dijkstra(graph, 0);

    return 0;
}

  这是最简单的实现方法,用一个链表或者数组来存储所有顶点的集合 Q,所以搜索 Q 中最小元素的运算(Extract-Min(Q))只需要线性搜索 Q 中的所有元素。这样的话算法的运行时间是 O(n^2)。

  对于边数少于 n2 的稀疏图来说,我们可以用邻接表来更有效的实现该算法。同时需要将一个二叉堆或者斐波纳契堆用作优先队列来查找最小的顶点(Extract-Min)。当用到二叉堆的时候,算法所需的时间为O((m + n)log n),斐波纳契堆能稍微提高一些性能,让算法运行时间达到O(m + n log n)。然而,使用斐波纳契堆进行编程,常常会由于算法常数过大而导致速度没有显著提高。

参考:

https://zh.wikipedia.org/wiki/%E6%88%B4%E5%85%8B%E6%96%AF%E7%89%B9%E6%8B%89%E7%AE%97%E6%B3%95

http://www.nocow.cn/index.php/Dijkstra%E7%AE%97%E6%B3%95

http://baike.baidu.com/view/1712262.htm?fromtitle=Dijkstra%E7%AE%97%E6%B3%95&fromid=215612&type=syn

版权声明:本文为博主原创文章,未经博主允许不得转载。

图算法(1):Dijkstra's algorithm

时间: 2024-10-21 11:25:20

图算法(1):Dijkstra's algorithm的相关文章

Geeks : Dijkstra’s Algorithm for Adjacency List Representation 最短路径

最短路径的O(ElgV)的解法. 使用邻接表存储图,使用堆操作选取下一个最小路径点. 本题的难度并不在最短路径本身这个算法,而是在于堆的操作: 1 使用双重指针操作堆的节点,可以省去直接复制操作堆节点,提高效率,并且这才是有效操作动态地址数据的方法,不用双重指针,我思考了下,觉得更加不好做. 2 使用一个数组记录当前顶点在堆中的位置,相当于一个hash表了,可以需要的时候,直接从表中查找表示顶点的堆节点在堆中的位置,要记得更新节点时维护好这个表. 3 释放内存的时候注意,弹出堆的节点可以马上释放

广度优先搜索(BreadthFirstSearch)&amp; 迪克斯特拉算法 (Dijkstra&#39;s algorithm)

BFS可回答两类问题: 1.从节点A出发,有前往节点B的路径吗? 2.从节点A出发,前往节点B的哪条路径经过的节点最少? BFS中会用到“队列”的概念.队列是一种先进先出(FIFO, first in first out)的数据结构,与栈不同,栈是后进先出(LIFO, last in first out)的数据结构. 还会用到“字典”的概念.字典在现在很多语言中都存在且广泛使用,字典中的元素是一组<键(key),值(value)>对,key的值是不可以重复的.关于字典的详细内容,网上有很多资料

【图算法】Dijkstra算法及变形

图示: 模版: 1 /* 2 Dijkstra计算单源最短路径,并记录路径 3 4 m个点,n条边,每条边上的权值非负,求起点st到终点et的最短路径 5 6 input: 7 n m st et 8 6 10 1 6 9 1 2 6 10 1 3 2 11 1 4 1 12 2 3 6 13 2 5 3 14 3 4 2 15 3 5 2 16 3 6 4 17 4 6 5 18 5 6 3 19 20 output: 21 6 22 1-->4-->6 23 */ 24 25 26 27

最短路径问题的Dijkstra算法

问题 是由荷兰计算机科学家艾兹赫尔·戴克斯特拉提出.迪科斯彻算法使用了广度优先搜索解决非负权有向图的单源最短路径问题,算法最终得到一个最短路径树>    .该算法常用于路由算法或者作为其他图算法的一个子模块. 这个算法的python实现途径很多,网上能够发现不少.这里推荐一个我在网上看到的,本来打算自己写,看了这个,决定自己不写了,因为他的已经太好了. 以下代码来自网络,但是我不能写来源,因为写了来源网址,这里就不让我发出这篇文章.这不是逼着我剽窃吗? 解决(Python) #!/usr/bin

[Algorithm] A* Search Algorithm Basic

A* is a best-first search, meaning that it solves problems by searching amoung all possible paths to the solution(goal) for the one that incurs the smallest cost(least distance, shortest time, etc.), and among these paths it first considers the one t

【转】Fibonacci 斐波纳契堆优化 Dijkstra 最短路径算法

话不多说,拿来主义,直接上代码! PS:打印最短路径我还不晓得怎么加,如有哪位大神知道,还请mark一下! 1 /*********************************************************************** 2 * File: FibonacciHeap.java 3 * Author: Keith Schwarz ([email protected]) 4 * 5 * An implementation of a priority queue

Dijkstra 单源最短路径算法

Dijkstra 算法是一种用于计算带权有向图中单源最短路径(SSSP:Single-Source Shortest Path)的算法,由计算机科学家 Edsger Dijkstra 于 1956 年构思并于 1959 年发表.其解决的问题是:给定图 G 和源顶点 v,找到从 v 至图中所有顶点的最短路径. Dijkstra 算法采用贪心算法(Greedy Algorithm)范式进行设计.在最短路径问题中,对于带权有向图 G = (V, E),Dijkstra 算法的初始实现版本未使用最小优先

What are the 10 algorithms one must know in order to solve most algorithm challenges/puzzles?

QUESTION : ANSWER: Dynamic Programming (DP) appears to account for a plurality (some estimate up to a third) of contest problems. Of course, DP is also not a single algorithm that you can just learn once and retain, so maybe this doesn't answer your

2018.2.25-26 algo part3 greedy algorithm

这周讲初级的greedy alorithm,greedy algorithm是一种算法思想,思路是每一步都做在当时看上去是最优的事情,那么很多步下来,最后得到的方案可能也是个比较不错的方案(虽然可能不是最优).之前接触过的knapsack problem和dijkstra's algorithm都是greedy algorithm的体现. 先讲的一个问题是scheduling application,就是说有一系列事务需要处理,每件事物有各自的优先级和处理时间,那么如何得到一个最优的schdul