单源最短路径(Dijkstra)算法

来源:互联网 发布:忆江南的知作者怎么写 编辑:程序博客网 时间:2024/05/21 20:29

Dijkstra 算法是一种贪心算法。

假定源点为 u,顶点集合 V 被划分为两部分:S 和 V-S,其中 S中的顶点到源点的最短路径的长度已经确定,V-S中的顶点到源点的最短路径待定。

思想

1. 每次从 V-S 中选择一个距离 源点最近的顶点,将其加入到 S 中,并从 V-S 中删除这个顶点;

2. 因为 S 中加入了新的顶点,更新 V-S 中其他所有点顶到源点的距离;

3. 当 V-S 为空时算法结束。


C++ 代码

#include <iostream>#include <limits>#include <vector>using namespace std;const int INFINITE = numeric_limits<int>::max();/* n:顶点个数(从 1 开始计数) * u:源点 * C:带权邻接矩阵 * dist:记录某顶点与源点 u 的最短路径长度 * p:记录某顶点到源点的最短路径上的该顶点的前驱顶点*/void Dijkstra(int n, int u, float *dist, int *p, int **C) {    bool s[n];// 用于标记顶点是否加入了 S    for(int i=1; i <= n; i++) {        s[i] = false;// 处源点外,所有顶点都还未加入 S,在 V-S 中        dist[i] = C[u][i];// 初始化所有其他顶点到源点的距离        // 初始化所有其他顶点到源点的前驱        if(INFINITE == dist[i]) {            p[i] = -1;        } else {            p[i] = u;        }    }    s[u] = true;// 源点 u 默认加入 S    dist[u] = 0;// 源点 u 到自己的距离为 0    for(int i=1; i <= n; i++) {// 遍历所有其他顶点        int minDist = INFINITE;        int t = u;        for(int j=1; j <= n; j++) {// 在 V-S 中寻找距离源点最近的点 t            if(!s[j] && (dist[j]<minDist)) {                t = j;                minDist = dist[j];            }        }        if(t == u)// 表示剩余的顶点要么到源点不可达,要么已经全部找到了最短距离都加入到 S 了。            break;        s[t] = true;// 找到这个顶点后,将其加入到 S        for(int j=1; j <= n; j++) {// 因为有新的顶点加入到 S,故更新其他所有未加入的顶点(V-S中的顶点)到源点的距离和前驱            if(!s[j] && C[t][j]<INFINITE)                if(dist[j] > (dist[t]+C[t][j])) {                    dist[j] = dist[t]+C[t][j];                    p[j] = t;                }        }    }}int** getMatC(int n, int m) {// 输入顶点个数,边个数,得到有向带权邻接矩阵    int **C = new int*[n+1];    for(int i=0; i <= n; i++) {        C[i] = new int[n+1]();        for(int j=0; j <= n; j++)            if(i != j)                C[i][j] = INFINITE;    }    for(int i=1; i <= m; i++) {        int x, y;        int distance = 0;        cin >> x >> y >> distance;        if(1 <= x && x <= n && 1 <= y && y <= n)            C[x][y] = distance;    }    return C;    // 输入样例:(有向带权图)    //   | 1  2  3  4  5    //---------------------    // 1 | 0  8  32 ∞  ∞    // 2 | 12 0  16 15 ∞    // 3 | ∞  29 0  ∞  13    // 4 | ∞  21 ∞  0  7    // 5 | ∞  ∞  27 19 0    /* 即:    5 11 1    1 2 8    1 3 32    2 1 12    2 3 16    2 4 15    3 2 29    3 5 13    4 2 21    4 5 7    5 3 27    5 4 19    */}vector<int> getPath(int* p, int u, const int& v) {    vector<int> path;    path.push_back(v);    int s = p[v];    while(s != u) {        path.push_back(s);        s = p[s];    }    path.push_back(s);    return path;}void printVec1D(const vector<int>& vec) {    if(!vec.empty()) {        for(int i = vec.size()-1; i > 0; i--)            cout << vec[i] << " -> ";        cout << vec[0] << endl;    }}int main() {    int n/*顶点数*/, m/*边数*/, u/*源点*/;    cin >> n >> m >> u;    int **C = getMatC(n, m);    if(NULL != C) {        float dist[n+1];// 存放距离        int path[n+1];// 存放前驱        Dijkstra(n, u, dist, path, C);        for(int i=1; i <= n; i++) {            if(INFINITE != dist[i]) {                if(i != u) {                    cout << "顶点 " << u << " 到顶点 " << i << " 的距离是: " << dist[i] << endl;                    cout << "路径为:";                    printVec1D(getPath(path, u, i));                }            } else                cout << "顶点 " << u << " 到顶点 " << i << " 不可达!" << endl;        }    }    return 0;}

输入样例对应的输出结果:




2 0
原创粉丝点击