1. 程式人生 > >Prim演算法及其優化

Prim演算法及其優化

來源自我的部落格

#include <stdio.h>

int main(){
    int n, m;
    scanf("%d%d", &n, &m);

    int e[10][10];  // 任意兩點間直接距離
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= n; j++){
            if (i == j) e[i][j] = 0;
            else e[i][j] = inf;
        }
    }

    int t1, t2, t3;
    for
(int i = 1; i <= m; i++){ scanf("%d%d%d", &t1, &t2, &t3); e[t1][t2] = t3; e[t2][t1] = t3; } int dis[10]; // 生成樹到各個頂點的距離,初始只表示1號點到各個頂點的距離 for (int i = 1; i <= n; i++){ dis[i] = e[1][i]; } int book[10] = {0}; // 為1表示頂點已加入生成樹 int
count = 0; // 生成樹中頂點數量 int sum = 0; // 生成樹的權值和 // 將1號頂點加入生成樹 book[1] = 1; count++; // 核心部分,等待所有結點加入生成樹 while (count < n){ // 在未加入生成樹的結點中尋找離生成樹最近的結點 int min = INT_MAX; for (int i = 1; i <= n; i++){ if (book[i] == 0 && dis[i] < min){ min = dis[i]; j = i; } } // 將其加入生成樹
book[j] = 1; count++; sum += dis[j]; // 掃描當前頂點j所有的邊,以j為中間點,更新生成樹到每一個非樹頂點的距離 for (int k = 1; k <= n; k++){ if (book[k] == 0 && dis[k] > e[j][k]){ dis[k] = e[j][k]; } } } printf("%d\n", sum); return 0; }
// 鄰接表儲存圖,用堆來選新邊
// 複雜度從O(N^2)變成O(MlogN)

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

int heap[20], pos[20], size;  // heap用來儲存堆,pos用來儲存每個頂點在堆中的位置,size表示堆大小

// 交換堆中兩個結點
void swap(int x, int y){
    // 直接交換值
    int t = heap[x];    
    heap[x] = heap[y];
    heap[y] = t;

    // 更新pos
    t = pos[heap[x]];
    pos[heap[x]] = pos[heap[y]];
    pos[heap[y]] = t;
}

// 堆的向下調整函式
void siftDown(int i){
    // i表示向下調整的堆結點編號
    int flag = 1;   // 為1表示需要繼續向下調整
    while (i * 2 <= size && flag == 1){
        int t = i;
        // 如果兒子更小,則指向兒子
        if (dis[heap[t]] > dis[heap[i * 2]]){
            t = i * 2;
        }
        if (i * 2 + 1 <= size && dis[heap[t]] > dis[heap[i * 2 + 1]]){
            t = i * 2 + 1;
        }

        // 判斷最小結點是當前結點還是在子結點上
        if (t != i){
            // 如果是在子結點上
            swap(t, i); // 交換兩個結點
            i = t;  // 當前結點下沉
        }
        else{
            // 如果當前就是最小結點,就不用繼續調整了
            flag = 0;
        }
    }
}

// 堆向上調整函式
void siftUp(int i){
    int flag = 1;
    if (i == 1) return; // 堆頂不需要調整
    while (i != 1 && flag == 1){
        // 判斷是不是比父結點小
        if (dis[heap[i]] < dis[heap[i / 2]]){
            swap(i, i / 2);
        }
        else{
            flag = 1;
        }
        i /= 2; // 上升到父結點
    }
}

// 從堆頂取元素
int pop(){
    int t = heap[1];    // 記錄堆頂元素
    heap[1] = heap[size];   // 將最後一個結點提到堆頂
    pos[heap[1]] = 1;   // 更新pos中的位置資訊
    size--;
    siftDown(1);
    return t;
}

int main(){
    int n, m;
    scanf("%d%d", &n, &m);

    int u[20], v[20], w[20];
    for (int i = 1; i <= m; i++){
        scanf("%d%d%d", &u[i], &v[i], &w[i]);
    }

    // 無向圖,所有邊反向儲存一次
    for (int i = m + 1; i <= 2 * m; i++){
        u[i] = v[i - m];
        v[i] = u[i - m];
        w[i] = w[i - m];
    }

    int first[20];  // 表示頂點對應的第一條鄰接邊, -1表示到頭了
    for (int i = 1; i <= n; i++){
        first[i] = -1;
    }
    // 把邊都接上
    for (int i = 1; i <= 2 * m; i++){
        next[i] = first[u[i]];  // 第i條邊頭插上去
        first[u[i]] = i;    // 使第i條邊為u[i]結點的第一條邊
    }

    int book[20] = {0}; // 為1表示已經加入生成樹
    int count = 0;
    int sum = 0;

    // 加入1號頂點到生成樹
    book[1] = 1;
    count++;

    int dis[20] = {0};  // 生成樹到各個頂點的初始距離,開始為1號頂點到各個頂點的初始距離
    dis[1] = 0;
    for (int i = 2; i <= n; i++) dis[i] = INT_MAX;

    // 開始只要將1號結點的鄰接點距離更新到dis中即可
    int k = first[1];   // 1號結點的第一條鄰接邊
    while (k != -1){
        dis[v[k]] = w[k];
        k = next[k];
    }

    // 初始化堆
    size = n;
    for (int i = 1; i <= size; i++) {
        h[i] = i;   // 初始化堆每個結點儲存初始編號
        pos[i] = i;     // 每個圖結點在堆中初始位置
    }

    // 調整堆
    for (int i = size / 2; i >= 1; i--){
        siftDown(i);    // 從第一個非葉結點向下調整
    }

    pop();  // 將堆頂的1號結點取出

    // 核心部分,等待所有結點加入到生成樹
    while (count < n){
        int j = pop();  // 取出堆頂元素,因為其必然離生成樹最近,所以可以加入生成樹
        book[j] = 1;
        count++;
        sum += dis[j];

        // 掃描剛加入的結點j相鄰的邊,更新其到生成樹的距離
        int k = first[j];   // j的第一條相鄰邊
        while (k != -1){
            if (book[v[k]] == 0 && dis[v[k]] > w[k]){
                dis[v[k]] = w[k];

                // 更新某點到生成樹距離後要在堆中向上調整
                siftUp(pos[v[k]]);
            }
            k = next[k];
        }
    }

    printf("%d\n", sum);

    return 0;
}