算法筆記---Kruskal算法

Kruskal算法:用來求解最小生成樹的算法,同樣使用貪心思想,只不過是邊的權值最小來貪心。

Prim算法和Kruskal算法都是求解最小生成樹的算法
只是Prim算法在執行時,是枚舉圖中所有結點,故比較適用稠密圖(即結點少,邊多),而Kruskal算法在執行時,是枚舉所有邊,故比較適用稀疏圖(即結點多,邊數少)。

Kruskal算法:
1、構造結構體,裏面存放邊的起始結點,終止結點,邊的權值。

struct edge{
int start;//起始結點
int end;//終止結點
int weight;//邊的權值
};

2、將邊按照從小到大排序。
3、枚舉所有邊,若邊的起始結點和終止結點不在一個集合中。則合併兩個結點,然後把當前的邊加入到最小生成樹中,並更新最小生成樹的總的邊權。
4、重複上述過程,直到最小生成樹的中有 n - 1 條邊,n爲結點數,退出循環。
5、檢查最小生成樹的邊的條數是否爲 n - 1,若不是,則表示該圖爲非連通圖,返回-1,否則返回最小生成樹總的邊權。

樣例:

輸入:
6 10//6個頂點,10條邊。以下10行爲10條邊
0 1 4//邊0->1與1->0的邊權爲4,下同
0 4 1
0 5 2
1 2 1
1 5 3
2 3 6
2 5 5
3 4 5
3 5 4
4 5 3
輸出:
11

解題思路;
使用並查集來表示兩個結點是否在一個集合中,若不在,就合併(這是並查集的方法)
若並查集不是很清楚的,可以看 算法筆記—並查集 這部分。

下面爲實現代碼:

#include<iostream>
#include<algorithm>
using namespace std;
const int max_v = 110;//點的最大值
const int max_e = 10010;//邊的最大值
struct edge
{
    int start;//起始結點
    int end;//終止結點
    int weight;//兩結點之間的邊
}E[max_e];//表示最多有max_e 條邊

bool cmp(edge e1,edge e2){
    return e1.weight < e2.weight;
}

int father[max_v];//並查集數組

/**
 * @description: 查找當前結點的父結點
 * @param : 要查找的結點
 * @return: 返回x的根結點
 */
int find_father(int x){
    int temp = x;//暫時存放需要查詢的結點
    while (x != father[x])
    {
        x = father[x];
    }
    //此時 temp 的根結點爲 x

    //下面爲路徑壓縮
    while (temp != father[temp])
    {
        int z = temp;
        temp = father[temp];
        father[z] = x;
    }
    return x;
}

/**
 * @description: kruskal算法
 * @param : n 表示結點數; m 表示邊數
 * @return: 返回最小生成樹的邊權之和
 */
int kruskal(int n,int m){
    int result = 0;//最小生成樹的邊權之和
    int edge_num = 0;//表示當前最小生成樹的邊數
    //初始化father數組
    for(int i = 0;i < n;i++){
        father[i] = i;
    }
    sort(E,E + m,cmp);//將邊的權值按照從小到大的順序排列
    //枚舉所有的邊
    for(int i = 0;i < m;i++){
        int start_father = find_father(E[i].start);
        int end_father = find_father(E[i].end);
        if(start_father != end_father){
            father[start_father] = end_father;//合併集合
            result += E[i].weight;
            edge_num++;
            if(edge_num == n - 1){
                //邊數等於結點數 - 1 即已經爲最小生成樹邊的最大值
                break;
            }
        }
    }
    if(edge_num != n - 1){
        //表示給定的圖不是連通圖
        return -1;
    }
    return result;
}

int main(){
    int n,m;
    cin >> n >> m;
    for(int i = 0; i < m; i++){
        cin>>E[i].start>>E[i].end>>E[i].weight;
    }
    int result = kruskal(n,m);
    cout<<result<<endl;
    system("pause");
    return 0;
}

運行結果:

運行結果

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