圖論經典算法——Dijkstra算法

1. 解決問題

目的是求某一頂點到其餘各個頂點【最短路徑】

2. Dijkstra算法思想

假設一共有兩個頂點集合 ST ,集合 S 中存放圖中已找到最短路徑的頂點,集合 T 中存放圖中的剩餘頂點。

初始狀態時,S 中只包含源點 V0,然後不斷通過從集合 T 中選取到頂點 V0 路徑長度最短的頂點 Vu 併入集合 S 中。

集合 S 每併入一個新的頂點Vu ,都要修改頂點 V0 到集合 T 中頂點的最短路徑長度值。

不斷重複這個過程,直到所有頂點全部併入集合 S 中爲止。

3. 輔助數據結構

Set T[ ]:已找到最短路徑的結點集合。這裏爲了簡單起見用 int T[ ] 表示,當 T[i]==1 表示存在上述集合 S 中,T[i]==0 表示存在上述集合 T 中。

int pre[ ]:記錄V0Vi 的前驅結點 Vi-1

int dist[ ]:記錄V0Vi 的最短路徑的長度。

4. 圖的表示

這裏採用簡單的鄰接矩陣表示有向圖。

int graph[7][7] = {
        {0, 4, 6, 6, 0, 0, 0},
        {0, 0, 1, 0, 7, 0, 0},
        {0, 0, 0, 0, 6, 4, 0},
        {0, 0, 2, 0, 0, 5, 0},
        {0, 0, 0, 0, 0, 0, 6},
        {0, 0, 0, 0, 1, 0, 8},
        {0, 0, 0, 0, 0, 0, 0}
};

5. 代碼描述

5.1 judge()

判斷T中是否包含所有的頂點,如果是Set類型則不需要此函數,只需要 set.find(i) 即可。

bool judge(int n) {
    for (int i = 0; i < n; ++i) {
        if (T[i] == 0)
            return false;
    }
    return true;
}

5.2 minIndex()

返回 dist[ ] 數組中所有路徑距離最小的頂點下標。

int minIndex(int n) {
    int index = -1;
    int min = INT_MAX;
    for (int i = 0; i < n; i++) {
        if (T[i] == 0 && dist[i] < min) {
            min = dist[i];
            index = i;
        }
    }
    return index;
}

5.3 shortestPath()

第一步:直接可達的頂點,用距離來初始化dist,dist[start]=0,可直達的把距離記錄下來作爲待定值。

第二步:從待定的距離表中找到最小值min以及對應的頂點 M ,這個值可以作爲確定值,爲什麼?(因爲不可能會有圖中任何一個頂點 x ,由 start 經過 x 再到頂點 M 產生的距離,要比直接從 start 到 M 更小)

第三步:看這個新確定的頂點的出度,看看從源點出發是經過這個頂點到其鄰居近還是直達更近,如果更近就要更新。

void shortestPath(int start, int n) {
    pre[start] = start;
    dist[start] = 0;
    T[start] = 1;
    
	//step1
    for (int i = 0; i < n; i++) {
        if (i != start) {   
            if (graph[start][i] == 0)
                dist[i] = INT_MAX;
            else if (graph[start][i] > 0) {
                dist[i] = graph[start][i];
                pre[i] = start;
            }
        }
    }

    while (judge(n) == false) { 
    	//step2
        int index = minIndex(n);
        T[index] = 1;
        if (judge(n) == true)
            break;

        //step3
        for (int neighbor = 0; neighbor < n; neighbor++) {
            int weight = graph[index][neighbor];
            if (weight > 0 && (dist[index] + weight) < dist[neighbor]) {
                dist[neighbor] = dist[index] + weight;
                pre[neighbor] = index;
            }
        }
    }
}

5.4 main()

應該倒着輸出 pre[ ] ,因爲存的是前驅結點,也可以用棧處理一下。

int main() {
    int n = sizeof(graph[0]) / sizeof(int);
    shortestPath(0, n);
    for (int i = 0; i < n; ++i) {
        cout << dist[i] << " ";
    }
    cout << endl;
    for (int i = 0; i < n; ++i) {
        cout << pre[i] << " ";
    }
}

6.總結

Dijkstra算法是圖論中經典的求最短路徑的算法,筆者在學習過程中發現它運用的是動態規劃的思想,此外圖論還有拓撲排序,Prim、Kruskal求最小生成樹算法、求任意兩點間的最短路徑的Floyd算法,以及求關鍵路徑的算法,這些對於考研和工作來說都是必須掌握的數據結構算法的基本知識,希望與大家共勉!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章