迪科斯彻算法

迪科斯彻算法(Dijkstra)是由荷兰计算机科学家艾兹格·迪科斯彻(Edsger Wybe Dijkstra)发明的。算法解决的是有向图中单个源点到其他顶点的最短路径问题。举例来说,如果图中的顶点表示城市,而边上的权重表示著城市间开车行经的距离,迪科斯彻算法可以用来找到两个城市之间的最短路径。

迪科斯彻算法的输入包含了一个有权重的有向图 G,以及G中的一个来源顶点 S。我们以 V 表示 G 中所有顶点的集合。每一个图中的边,都是两个顶点所形成的有序元素对。(u, v) 表示从顶点u  v 有路径相连。我们以 E 所有边的集合,而边的权重则由权重函数 w: E  [0, ] 定义。因此,w(u, v) 就是从顶点 u 到顶点 v 的非负花费值(cost)。边的花费可以想像成两个顶点之间的距离。任两点间路径的花费值,就是该路径上所有边的花费值总和。已知有 V 中有顶点 s  tDijkstra 算法可以找到 s  t 的最低花费路径(例如,最短路径)。这个算法也可以在一个图中,找到从一个顶点 s 到任何其他顶点的最短路径。

 

算法描述

这个算法是通过为每个顶点 v 保留目前为止所找到的从sv的最短路径来工作的。初始时,原点 s 的路径长度值被赋为 0 d[s] = 0),同时把所有其他顶点的路径长度设为无穷大,即表示我们不知道任何通向这些顶点的路径(对于 V 中所有顶点 v  s d[v] = ∞)。当算法结束时,d[v] 中储存的便是从 s  v 的最短路径,或者如果路径不存在的话是无穷大。 Dijkstra 算法的基础操作是边的拓展:如果存在一条从 u  v 的边,那么从 s  v 的最短路径可以通过将边(u, v)添加到尾部来拓展一条从 s  u 的路径。这条路径的长度是 d[u] + w(u, v)。如果这个值比目前已知的 d[v] 的值要小,我们可以用新值来替代当前 d[v] 中的值。拓展边的操作一直执行到所有的 d[v] 都代表从 s  v 最短路径的花费。这个算法经过组织因而当 d[u] 达到它最终的值的时候每条边(u, v)都只被拓展一次。

算法维护两个顶点集 S  Q。集合 S 保留了我们已知的所有 d[v] 的值已经是最短路径的值顶点,而集合 Q 则保留其他所有顶点。集合S初始状态为空,而后每一步都有一个顶点从 Q 移动到 S。这个被选择的顶点是 Q 中拥有最小的 d[u] 值的顶点。当一个顶点 u  Q 中转移到了 S 中,算法对每条外接边 (u, v) 进行拓展。

 

C++代码实现

Dijkstra算法求一点到其他点的最短路径。关系矩阵可以写成半角阵,因为无向图矩阵是对称的,x代表断路,数字代表点之间的距离,图上一共8个点,自己到自己的距离是0。例如,书P166 7.3 求图7-13所示途中定点V0V5的最短路径。我们先将图7-13转换为如下矩阵:

0 1 4 X X X

1 0 2 7 5 X

4 2 0 X 1 X

X 7 X 0 3 2

X 5 1 3 0 6

X X X 2 6 0

详细代码如下:

#include <iostream>

#include <vector>

#include <list>

#include <iterator>

#include <algorithm>

#include <numeric>

#include <functional>

#include <climits>

using namespace std;

int map[6][6]={          // map : (map)(用邻接矩阵(adjacent matrix)表示)

    {0,1,4,'X','X','X'},     //'x'ASCII码为120,显然远大于本题的两顶点见最大距离

{1,0,2,7,5,'X'},

{4,2,0,'X',1,'X'},

{'X',7,'X',0,3,2},

{'X',5,1,3,0,6},

{'X','X','X',2,6,0}

};

int n;                 // n : 顶点个数

int s;                 // s : 源点(source)

vector<bool> known;        // known : 各点是否知道最短路径

vector<int> dist;        // dist : 源点s到各点的最短路径长

vector<int> prev;        // prev : 各点最短路径的前一顶点

void Dijkstra()            // 贪心算法(Greedy Algorithm)

{

    known.assign(n, false);

    dist.assign(n, INT_MAX);

    prev.resize(n);            // 初始化knowndistprev

    dist[s] = 0;            // 初始化源点s到自身的路径长为0

    while(1)

    {

        int min = INT_MAX, v = s;

        for (int i = 0; i < n; ++i)

            if (!known[i] && min > dist[i])

                min = dist[i], v = i;    // 寻找未知的最短路径长的顶点v

        if (min == INT_MAX) break;        // 如果找不到,退出;

        known[v] = true;                // 如果找到,将顶点v设为已知,

        for (int w = 0; w < n; ++w)        // 遍历所有v指向的顶点w

            if (!known[w] && map[v][w] < INT_MAX && dist[w] > dist[v] + map[v][w])

                dist[w] = dist[v] + map[v][w], prev[w] = v;    // 调整顶点w的最短路径长dist和最短路径的前一顶点 prev

    }

}

void Print_SP(int v)

{

     if (v != s) Print_SP(prev[v]);

     cout << v << " ";

}

int main()

{

    n=6;

    s = 0;

    Dijkstra();

    copy(dist.begin(), dist.end(), ostream_iterator<int>(cout, " ")); cout << endl;

    for (int i = 0; i < n; ++i)

        if(dist[i] != INT_MAX)

        {

            cout << s << "->" << i << ": ";

            Print_SP(i);

            cout << endl;

        }

   

    system("pause");

    return 0;

}

 

运行结果

转载于:https://www.cnblogs.com/ituff/archive/2011/03/16/2858557.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值