1. 程式人生 > >最小生成樹兩種演算法比較與實現

最小生成樹兩種演算法比較與實現

Kruskal演算法 :(並查集)
時間複雜度O(elog2e),適合簡單圖。
演算法步驟:

  1.構造一個有n個頂點的無邊子圖;

  2.從原圖選擇邊權最小的邊加入該子圖,直至子圖成為一棵樹;

  3.邊能加入子圖的條件是,邊的兩個端點u,v還未連通,Kruskal演算法中運用並查集的查詢來詢問兩個頂點是否連通;

  Kruskal演算法的本質是,通過樹的合併(不斷加邊,構成子樹),來構建完整的生成樹。

就是先將邊的權按從小到大的順序排起來,依次選出邊,判斷兩點是否在一個集合中,如果在,看下一條邊,如果不在,則將兩點合併,這條邊為最小生成樹的一條邊。

#include<iostream>
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN = 505; int a[MAXN][MAXN]; typedef struct graph{ int u,v,cost; friend bool operator <(const graph &a,const graph &b) { return a.cost<b.cost; } }; graph edge[MAXN*MAXN]; int
b[MAXN]; int c[MAXN]; int find2(int x) { int i = x; while(x != b[x]) { x = b[x]; } int p; while(i != b[i]) { p = b[i]; b[i] = x; i = p; } return x; } void find1(int n,int m) { int p = find2(n); int q = find2(m); if(p!=q) { b[q]=b[p]; } } int
main() { int t; cin>>t; while(t--) { int n,i,j; scanf("%d",&n); for(i = 1;i <= n;++i) { b[i] = i; } for(i=1;i<=n;++i) { for(j=1;j<=n;++j) { scanf("%d",&a[i][j]); } } int k=1; for(i = 1;i <= n;++i) { for(j = i + 1;j <= n;++j) { edge[k].u = i; edge[k].v = j; edge[k].cost = a[i][j]; ++k; } } sort(edge+1,edge+k); /*for(i = 1;i < k;++i) { printf("%d %d %d\n",edge[i].u,edge[i].v,edge[i].cost); }*/ int ptr = 0; for(i = 1;i < k;++i) { if(find2(edge[i].u)!=find2(edge[i].v)) { find1(edge[i].u,edge[i].v); c[ptr++] = i; } } int mx = 0; for(i = 0;i < ptr;++i) { mx = (mx < edge[c[i]].cost)?edge[c[i]].cost:mx; } printf("%d\n",mx); } return 0; }

Prim演算法:(離散書上講的方法)
時間是複雜度O(n2),適合稠密圖。
Prim演算法的基本步驟:

  1.初始化點集 V={x};

  2.找到邊(u,v)滿足:u∈點集V,v不∈點集V;

  3.選取2.中滿足條件的邊中最小的一條加入生成樹,並把v加入點集V,重複執行,直至原圖所有的點都加入點集V,就得到了一棵最小生成樹。

Tips:關於如何快速找到可以新增到生成樹的邊:

                可以維護一個數組lowcost[i…j]記錄點集V到各個頂點的最小邊,即可快速找到邊,且每當有新的點加入點集V時,該陣列都要更新一次。
  #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 2005;
const int inf = 0x3f3f3f3f;
int mp[MAXN][MAXN];
int lowcost[MAXN];
bool vis[MAXN];
int Prime(int n)
{
    int i,j;
    for(i = 1;i <= n;++i)
    {
        lowcost[i] = mp[1][i];
    }
    vis[1] = true;
    lowcost[1] = 0;
    int ans = 0;
    for(i = 0;i < n - 1;++i)
    {
        int min = inf;
        int k = 0;
        for(j = 1;j <= n;++j)
        {
            if(!vis[j]&&min > lowcost[j])
            {
                min = lowcost[j];
                k = j;
            }
        }
        vis[k] = true;
        ans = (ans > min)?ans:min;
        for(j = 1;j <= n;++j)
        {
            if(!vis[j]&&lowcost[j] > mp[k][j])
                 lowcost[j] = mp[k][j];
        }
    }
    return ans;
}
int main()
{
    int n,m;
    while(~scanf("%d %d",&n,&m))
    {
        int i,j;
        memset(mp,inf,sizeof(mp));
        memset(vis,false,sizeof(vis));
        for(i = 0;i < m;++i)
        {
            int x,y,z;
            scanf("%d %d %d",&x,&y,&z);
            if(mp[x][y] > z)//防止出現重邊現象
                mp[x][y] = z;
            if(mp[y][x] > z)
                mp[y][x] = z;
        }
        int ans = Prime(n);
        printf("%d\n",ans);
    }
    return 0;
}