1. 程式人生 > >最小生成樹之kruskal演算法(附程式碼)

最小生成樹之kruskal演算法(附程式碼)

prim演算法是通過找距離最近的節點來擴充最小生成樹的,稠密圖選擇prim演算法效率比較高,但是對於稀疏圖呢,prim演算法就顯的比較雞肋了。對於稀疏圖,有一個叫做kruskal的演算法。此演算法求稀疏圖的效率比較高,時間複雜度為O(ElogE)。

kruskal演算法主要通過找最小的邊來合併節點來一步步的生成樹的,他一開始將每個節點看成一棵樹,然後通過邊的關係來進行節點的合併,這裡需要對給出的邊進行升序排序,然後再檢查邊所聯絡的兩個節點是否在同一集合,如果不在同一集合則將兩個集合合併,直到整個森林變為一顆樹為止。

程式碼如下:
 

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const int maxn=1005;
int n,m;
struct edge
{
    int s,e;
    int len;
};
edge e[maxn];
int a[maxn];
vector <edge> ve; //記錄納入的邊
//初始化
int init ()
{
    for (int i=1;i<=n;i++)
        a[i]=i;
}
//查詢父節點
int finds(int re)
{
    if(re==a[re])
        return re;
    return a[re]=finds(a[re]);
}
//將兩個集合聯合
bool unit (int x,int y)
{
    int temp1=finds(x);
    int temp2=finds(y);
    if(temp1!=temp2)
    {
         a[temp1]=temp2;
         return true;
    }
    return false;
}
int compare (edge a,edge b)
{
    return a.len<b.len;
}
//krusal演算法
int kruskal ()
{
    //先進行排序
    sort(e,e+m,compare);
    int num=n;
    int sum=0;
    for (int i=0;i<m&&num>1;i++)
    {
        //判斷是否在同一集合中,在同一集合就不再計入
        if(unit(e[i].s,e[i].e))
        {
            ve.push_back(e[i]);
            sum+=e[i].len;
            num--;
        }
    }
    if(num==1)
        printf("最小生成樹的權值和為%d\n",sum);
    else
        printf("不存在最小生成樹\n");
}
//遍歷納入的邊
void traverse ()
{
    printf("納入的邊為:\n");
    for (int i=0;i<ve.size();i++)
        printf("%d<->%d 長度為%d\n",ve[i].s,ve[i].e,ve[i].len);
}
int main()
{
    scanf("%d%d",&n,&m);
    init();
    for (int i=0;i<m;i++)
    {
        scanf("%d%d%d",&e[i].s,&e[i].e,&e[i].len);
    }
    kruskal();
    traverse ();
    return 0;
}
/*執行結果
6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3
最小生成樹的權值和為12
納入的邊為:
3<->5 長度為1
3<->6 長度為1
1<->6 長度為2
2<->5 長度為2
2<->4 長度為6*/