1. 程式人生 > >利用Dijkstra演算法實現記錄每個結點的所有最短路徑

利用Dijkstra演算法實現記錄每個結點的所有最短路徑

最近在做PAT時發現圖論的一些題目需要對多條最短路徑進行篩選,一個直接的解決辦法是在發現最短路徑的時候就進行判斷,選出是否更換路徑;另一個通用的方法是先把所有的最短路徑記錄下來,然後逐個判斷。前者具有一定的難度並且不好排查BUG,因此我設計了一種基於Dijkstra的記錄所有最短路的簡捷演算法,用於解決此類題目。

我們知道,Dijkstra是解決單源最短路問題的,並且最基本的演算法僅能求出最短路的長度,而不能輸出路徑,本文基於Dinjkstra進行改進,使之能記錄源點到任意點的所有最短路徑。

使用vector<int>來記錄一條路徑,因為每個結點可能有多條最短路徑,因此把這些路徑都裝在一個vector中,因此可以用一個vector<vector<int> >來表示一個結點的所有最短路徑,把所有結點的最短路徑都存放起來,又需要一個vector容器,因此所有結點的所有最短路徑的集合可以用vector<vector<vector<int> > >來表示。

約定:結點編號為0到N-1,源點為0,到每個點的最短距離儲存在陣列minD[N]中。

在Dijkstra演算法初始化時,找出所有源點的鄰接點w並且把相應的最短距離minD[w]更新,同時初始化這些點w的第一條最短路徑0->w(實現方法為分別push_back 0和w)。接下來將會找到一個到源點最短的點v,並且把v併入集合,對v的所有未訪問的鄰接點,如果到達w的路徑(0->...->w)在包含v之後(0->...->v->w)變短,則刪除w之前所有的最短路徑,並且更新為到v的所有最短路徑加上w點(注意對每個到v的最短路徑都要這樣處理);如果到達w的路徑在包含v之後長度不變,說明發現了一條新的最短路徑,在w原來最短路徑容器的基礎上再壓入一個新的最短路徑,這條路徑為所有到v的最短路徑加上w點。

經過這樣的運算,就可以得到所有結點的所有最短路徑了,下面以一個例項對演算法進行測試,並且附上原始碼。

題目:求下圖的源點0到所有結點的最短路徑。


輸入:

5 8
2 4 1
0 1 3
0 2 6
1 3 2
1 4 1
3 4 1
3 2 1
0 4 4

輸出:


原始碼為:

#include <iostream>
#include <stdio.h>
#include <vector>

using namespace std;

#define MAX 1001
#define INF 99999999

int G[MAX][MAX];
int minD[MAX];
int minDist;
int finalSet[MAX];

int main()
{
    int N,M;
    int v1,v2;
    int len;
    cin >> N >> M;
    for(int i = 0; i < N; i++){
        finalSet[i] = 0;
        minD[i] = INF;
        for(int j = 0; j < N; j++)
            G[i][j] = INF;
    }
    for(int i = 0; i < M; i++){
        scanf("%d%d%d",&v1,&v2,&len);
        G[v1][v2] = G[v2][v1] = len;
    }

    vector<vector<vector<int> > > nodes(N);

    // 設0為源點,計算從0到所有點的所有最短路徑
    finalSet[0] = 1;
    minD[0] = 0;
    // 首先把所有源點鄰接點的最短距離初始化為源點到這些點的距離
    for(int i = 1; i < N; i++){
        if(G[0][i] != INF) {
            minD[i] = G[0][i];
            vector<vector<int> > minPaths;
            minPaths.clear();
            vector<int> pathList;
            pathList.clear();
            pathList.push_back(0);
            pathList.push_back(i);
            minPaths.push_back(pathList);
            nodes[i] = minPaths;
        }
    }

    // 從所有minD中找出最小的,併入集合S,重複N-1次(源點已經加入集合)
    for(int i = 1; i < N; i++){
        minDist = INF;
        int v = -1; // 記錄到源點記錄最小的結點
        for(int w = 1; w < N; w++){
            if(!finalSet[w] && minDist > minD[w]){
                minDist = minD[w];
                v = w;
            }
        }
        if(v == -1) break; // v = -1說明找不到點了,當圖不連通時才會出現這種情況
        // 已經找到了到源點最近的點v,將其併入集合,並且考慮原來的最短距離0->...->W在加入了v之後有沒有可能變短
        // 如果變短了,就更新為0->...>v->W
        finalSet[v] = 1;
        for(int w = 1; w < N; w++){
            if(!finalSet[w]){
                int newD = minDist + G[v][w];
                if(newD < minD[w]){
                    minD[w] = newD;
                    vector<vector<int> > minPathsV = nodes[v];
                    vector<int> pathList;
                    nodes[w].clear();
                    for(int index = 0; index < minPathsV.size(); index++){
                        pathList = minPathsV[index];
                        pathList.push_back(w);
                        nodes[w].push_back(pathList);
                    }

                }else if(newD == minD[w]){

                    vector<vector<int> > minPathsV = nodes[v];
                    vector<int> pathList;
                    for(int index = 0; index < minPathsV.size(); index++){
                        pathList = minPathsV[index];
                        pathList.push_back(w);
                        nodes[w].push_back(pathList);
                    }

                }
            }
        }
    }
    for(int i = 1; i < N; i++){
        cout << "------------" << endl;
        cout << "0 to "<< i << ":" << endl;
        cout << "The miniest distance:" << endl << minD[i] << endl;
        cout << "The possible paths:" << endl;
        vector<vector<int> >minPaths = nodes[i];
        int size = minPaths.size();
        vector<int> pathList;
        for(int j = 0; j < size; j++){
            pathList = minPaths[j];
            int pathSize = pathList.size();
            for(int k = 0; k < pathSize - 1; k++){
                cout << pathList[k] << "->";
            }
            cout << pathList[pathSize - 1] << endl;
        }

    }

    return 0;
}


相關推薦

利用Dijkstra演算法實現記錄每個結點所有路徑

最近在做PAT時發現圖論的一些題目需要對多條最短路徑進行篩選,一個直接的解決辦法是在發現最短路徑的時候就進行判斷,選出是否更換路徑;另一個通用的方法是先把所有的最短路徑記錄下來,然後逐個判斷。前者具有一定的難度並且不好排查BUG,因此我設計了一種基於Dijkstra的記錄所

Dijkstra演算法(有權圖單源路徑)

  從一個源點到其他各頂點的最短路徑問題稱為“單源最短路徑問題”。 最短路徑的最優子結構性質 該性質描述為:如果P(i,j)={Vi…Vk…Vs…Vj}是從頂點i到j的最短路徑,k和s是這條路徑上的一箇中間頂點,那麼P(k,s)必定是從k

Dijkstra演算法,有權單源路徑

與無權單源最短路徑相似,與層次遍歷相似;以遞增的順序依次收錄遇到的最短距離的頂點 int findmin(Graph G,int dist[],int collected[]) // 找到一個未被收錄的最小值 { int Min = MAXNUM; // 儲存最小值,初值為無窮大;

Dijkstra演算法--一個點到其餘點路徑

Dijkstra演算法 要求 求一個點(源點)到其餘各個頂點的最短路徑。 思路 先將源點到其餘各個點的路徑列出來dis[],找到最小值,這個最小值就是源點到這一點u的最短路徑,並標記已經找出,再以這個點開始,依次遍歷到其它點v,如果這個點u到

Dijkstra演算法求解無向圖的路徑

  Dijkstra演算法是典型的演算法。Dijkstra演算法是很有代表性的演算法。Dijkstra一般的表述通常有兩種方式,一種用永久和臨時標號方式,一種是用OPEN, CLOSE表的方式,這裡均採用永久和臨時標號的方式。注意該演算法要求圖中不存在負權邊。

Dijkstra演算法實現==2014hiho第23周短路問題

小Ho想了想說道:“唔……我覺得動態規劃可以做,但是我找不到計算的順序,如果我用f[i]表示從S到達編號為i的節點的最短距離的話,我並不能夠知道f[1]..f[N]的計算順序。” “所以這個問題不需要那麼複雜的演算法啦,我就稍微講講你就知道了!”小Hi道:“路的長度不可能為負數對不對?” “那是自然,畢竟人

迪傑斯特拉演算法處理無向圖中路徑的(dijkstra)Java實現(指定兩點,求短距離及路徑)

其實不是原創哈,我寫不出來。       如何求圖中V0到V5的最短路徑呢?         java實現的方式如下:         第一步,根據圖來建立權值矩陣:        int[][] W = {      {  0,   1,   4,  -1,  -

結點路徑之Floyd演算法原理詳解及實現

上兩篇部落格介紹了計算單源最短路徑的Bellman-Ford演算法和Dijkstra演算法。Bellman-Ford演算法適用於任何有向圖,即使圖中包含負環路,它還能報告此問題。Dijkstra演算法執行速度比Bellman-Ford演算法要快,但是其要求圖中不能包含負權重

結點路徑Floyd弗洛伊德演算法解析

        暑假,小哼準備去一些城市旅遊。有些城市之間有公路,有些城市之間則沒有,如下圖。為了節省經費以及方便計劃旅程,小哼希望在出發之前知道任意兩個城市之前的最短路程。         上圖中有4個城市8條公路,公路上的數字表示這條公路的長短。請注意這些公

貪心演算法之用優先佇列解決路徑問題(Dijkstra演算法

#include <iostream> #include <cstdio> #include <stack> #include <cstring> #include <queue> #include <cstdlib> using na

【資料結構與演算法】 有向圖的路徑實現

Goal: Practice the algorithms of shortest pathproblem. Task:用一個有向圖表示給定的n個(要求至少10個)城市(或校園中的一些地點)及其之間的道路、距離情況,道路是有方向的。要求完成功能:根據使用者輸入的任意兩個城

每對結點之間路徑的C++實現

轉載本部落格上原創文章者,請註明出處。 Dijkstra演算法和Bellman-Ford演算法只能計算出起始點到其他各點的最短路徑,但不能計算任意兩隊頂點之間的最短路徑。若真想利用這兩張演算法,可以來一個迴圈,每次讓不同的頂點成為起始頂點,這樣也可以解決,但這種方法效率比較

資料結構實驗34——求賦權圖中一個結點所有結點路徑的長度

Description給一個賦權圖(無向圖),求0號結點到其餘所有結點的最短路徑的長度。Input先輸入一個小於等於100的正整數n,然後輸入賦權圖的鄰接矩陣(10000表示無窮大,並且任意一條簡單路徑

設計一個演算法,輸出從u到v的所有路徑(採用鄰接表儲存)

思想:用path陣列存放路徑(初始為空),d表示路徑長度(初始為-1),查詢從頂點u到v的最短路徑過程如圖所示: 對應演算法如下: void FindPath(

Flody演算法(有權多源路徑問題)

多源最短路徑問題,即為求每一對頂點之間的最短路徑問題 演算法描述 演算法思想原理: Floyd演算法是一個經典的動態規劃演算法。用通俗的語言來描述的話,首先我們的目標是尋找從點i到點j的最短路徑。從動態規劃的

hdu2066 dijkstra多源點多終點求路徑

dijkstra演算法的思路: (1)找到最短距離已經確定的頂點,從它出發更新相鄰頂點的最短距離 (2)此後不再關心(1)中最短距離已經確定的頂點 最開始時只有起點的最短距離是確定的,而在未使用過的頂點中,距離d[i]最小的頂點就是最短距離已經確定的頂點,在不存在負邊的情況下d[i]不會

hdu2066 dijkstra多源點多終點求路徑

dijkstra演算法的思路: (1)找到最短距離已經確定的頂點,從它出發更新相鄰頂點的最短距離 (2)此後不再關心(1)中最短距離已經確定的頂點 最開始時只有起點的最短距離是確定的,而在未使用過的頂點中,距離d[i]最小的頂點就是最短距離已經確定的頂點,在不存在負邊的

求無向圖頂點之間的所有路徑

思路一: DFS,遇到終點之後進行記錄 輔助儲存: std::vector<int> tempPath; std::vector<std::vector<int>> totalPath; 實現: //查詢無向

簡單圖論:遍歷所有路徑

今天遇到了兩道要求遍歷所有最短路徑的題,我一直做不對的原因竟是我把無向圖當成了有向圖,鬱悶的要死。 解決遍歷所有最短路徑,其實思路很簡單,首先通過經典演算法[各種演算法,Dijkstra,bellman,floyd]求出最短路徑的長度,然後就只能DFS來找尋起始點、終點一樣

floyd演算法(求任意兩點間的路徑)

floyd演算法用於求任意兩點間的最短路徑,時間複雜度為O(n^3)。 通過多次呼叫 Digkstar 演算法同樣能解決這個問題,時間複雜度同樣為O(n^3),但floyd更簡潔,利於程式設計。 fl