資料結構學習筆記-迪傑斯特拉演算法

zeta186012發表於2024-06-08

最短路徑問題的經典解法-dijsktra演算法

問題描述:求從一個頂點到另一個頂點的最短路徑

【演算法設計思想】

Dijkstra演算法的設計思想基於以下關鍵概念和步驟,旨在找出圖中從一個給定的源頂點到其他所有頂點的最短路徑。這個演算法適用於有向和無向圖,只要圖的邊權重為非負值。

1. 初始化

  • dist[]為一個陣列,dist[i]表示從源頂點到頂點i的最短路徑估計。初始時,對所有idist[i]設為無窮大,除了源頂點自身,其值設為0。
  • SPTSet[](最短路徑樹集合)為一個集合,用於記錄已經被處理並且其最短路徑已經被找到的頂點。最開始,這個集合為空。

2. 選擇最小距離頂點

  • 在未處理的頂點集合中選擇一個距離最小的頂點u,即dist[u]是最小的,並且u不在SPTSet中。最開始,這將會是源頂點。

3. 更新距離值

  • 對於頂點u的每個鄰接頂點v,如果v不在SPTSet中,並且透過uv的路徑長度小於當前記錄的dist[v]的值,則更新dist[v]。更新規則是:dist[v] = dist[u] + weight(u, v),其中weight(u, v)是從uv的邊的權重。

4. 標記為已處理

  • 將頂點u新增到SPTSet中,表示u的最短路徑已經被找到。

5. 重複步驟

  • 重複步驟2至4,直到所有頂點都被處理,即SPTSet包含所有頂點。

【演算法描述】

// 尋找最短路徑樹集合中距離最小的頂點
int minDistance(int dist[], int sptSet[]) {
    // 初始化最小值
    int min = INT_MAX, min_index;

    for (int v = 0; v < V; v++)
        if (sptSet[v] == 0 && dist[v] <= min)
            min = dist[v], min_index = v;

    return min_index;
}

// 使用Dijkstra演算法找到圖中源點到所有頂點的最短路徑
void dijkstra(int graph[V][V], int src) {
    int dist[V]; // dist[i]會儲存源點到i的最短路徑距離
    int sptSet[V]; // sptSet[i]會為真如果頂點i在最短路徑樹中,或者最短距離從源點到i已經確認

    // 初始化所有距離為無窮大,sptSet[]為假
    for (int i = 0; i < V; i++)
        dist[i] = INT_MAX, sptSet[i] = 0;

    // 源點到自己的距離總是0
    dist[src] = 0;

    // 尋找所有頂點的最短路徑
    for (int count = 0; count < V - 1; count++) {
        // 從尚未處理的頂點集合中選取距離最小的頂點
        int u = minDistance(dist, sptSet);

        // 標記選取的頂點為已處理
        sptSet[u] = 1;

        // 更新選取頂點的鄰接頂點的距離值
        for (int v = 0; v < V; v++)

            // 更新dist[v]只有在以下情況:未在sptSet中,存在從u到v的邊,且源點到v的總距離小於當前dist[v]的值
            if (!sptSet[v] && graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v])
                dist[v] = dist[u] + graph[u][v];
    }

    // 列印構建的距離陣列
    printSolution(dist);
}

【完整的測試程式】

#include <stdio.h>
#include <limits.h>

// 頂點數量
#define V 9

// 尋找最短路徑樹集合中距離最小的頂點
int minDistance(int dist[], int sptSet[]) {
    // 初始化最小值
    int min = INT_MAX, min_index;

    for (int v = 0; v < V; v++)
        if (sptSet[v] == 0 && dist[v] <= min)
            min = dist[v], min_index = v;

    return min_index;
}

// 列印構建的距離陣列
void printSolution(int dist[]) {
    printf("頂點 \t 距離源點的距離\n");
    for (int i = 0; i < V; i++)
        printf("%d \t %d\n", i, dist[i]);
}

// 使用Dijkstra演算法找到圖中源點到所有頂點的最短路徑
void dijkstra(int graph[V][V], int src) {
    int dist[V]; // dist[i]會儲存源點到i的最短路徑距離
    int sptSet[V]; // sptSet[i]會為真如果頂點i在最短路徑樹中,或者最短距離從源點到i已經確認

    // 初始化所有距離為無窮大,sptSet[]為假
    for (int i = 0; i < V; i++)
        dist[i] = INT_MAX, sptSet[i] = 0;

    // 源點到自己的距離總是0
    dist[src] = 0;

    // 尋找所有頂點的最短路徑
    for (int count = 0; count < V - 1; count++) {
        // 從尚未處理的頂點集合中選取距離最小的頂點
        int u = minDistance(dist, sptSet);

        // 標記選取的頂點為已處理
        sptSet[u] = 1;

        // 更新選取頂點的鄰接頂點的距離值
        for (int v = 0; v < V; v++)

            // 更新dist[v]只有在以下情況:未在sptSet中,存在從u到v的邊,且源點到v的總距離小於當前dist[v]的值
            if (!sptSet[v] && graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v])
                dist[v] = dist[u] + graph[u][v];
    }

    // 列印構建的距離陣列
    printSolution(dist);
}

int main() {
    // 例子中圖的鄰接矩陣表示法
    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, 14, 10, 0, 2, 0, 0},
                       {0, 0, 0, 0, 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;
}

相關文章