Prim算法和Kruskal算法的基本思想和實現

這兩個都是求最小生成樹的算法,個人更喜歡Kruskal算法。

Prim算法

基本思想

有兩個集合,A是空集,B集合裏有現在圖中的所有邊。
將B中任意一點加入A集合,在這個點的所用通路中選擇一個權值最小的邊且這個邊到的點不在B集合之中。然後將這個點加入A集合,再進行上述步驟,直到將B集合變成空集。

代碼實現

#include<bits/stdc++.h>
using namespace std;
const int INF=1<<30;
const int maxn=110;
typedef struct {
    int mp[maxn][maxn];
    int number;
}Graph;
void prim(Graph &a)
{
    int min,i,j,k;
    int adj[maxn],lowcost[maxn];
    lowcost[0]=0;//值爲0代表已經放入A集合
    adj[0]=0;
    for(int i=1;i<a.number;++i){
        lowcost[i]=a.mp[0][i];//初始化lowcost和adj數組。lowcost代表現在點到各個點的距離
        adj[i]=0;
    }
    for(int i=1;i<a.number;++i){
        int min=INF;
        int j=1,k=0;
        while(j < a.number)
        {
            if( lowcost[j]!=0 && lowcost[j]<min){//在集合B中選出一個最小的邊
                min = lowcost[j];
                k=j;
            }
            j++;
        }
        cout<<"("<<adj[k]<<","<<k<<")"<<endl;
        lowcost[k]=0;
        for(int j=1;j<a.number;++j){
            if(lowcost[j]!=0 && a.mp[k][j]<lowcost[j]){
                lowcost[j]=a.mp[k][j];//因爲有新的點加入A集合,對應的數組更新
                adj[j]=k;
            }
        }
    }
}
int main()
{
    freopen("in.txt","r",stdin);
    Graph a;
    int v,e,x,y,w;
    cin>>v>>e;
    a.number=v;
    for(int i=0;i<v;++i)
        for(int j=0;j<v;++j)
        a.mp[i][j]=INF;
    for(int i=0;i<v;++i)
        a.mp[i][i]=0;
    for(int i=0;i<e;++i){
        cin>>x>>y>>w;
        a.mp[x][y]=w;
        a.mp[y][x]=w;
    }
    prim(a);
    return 0;
}

Kruskal算法

基本思路

學習Kruskal算法需要前置知識:並查集,一個簡單但實用的數據結構。
學習鏈接:並查集的基本思想和實現
將並查集應用在圖上還需要另外一個數據結構:邊集數組
就是一個三位數組,裏面存的是起始點,終止點,邊權。
將這個數組以邊權大小排序,小的在前。
然後按照順序將數組中存的點連接起來,形成集合。而連起點的前提條件就是不屬於一個集合。這個集合的判斷就需要用到並查集了。在連接的時候也需要用到並查集進行合併。
直到將所有的點都合併爲一個集合,循環結束。

代碼實現

#include<bits/stdc++.h>
using namespace std;
const int maxn=110;
typedef struct{
    int begin,end,weight;
}Edge;
int FindSet(int x,int *father)
{

    if(x!=father[x])
        father[x]=FindSet(father[x],father);
    return father[x];
}
bool Union(int x,int y,int *father,int *rank)
{
    x=FindSet(x,father);
    y=FindSet(y,father);
    if(x==y)//屬於同一集合,不進行合併操作
        return false;
    if(rank[x]>rank[y])
        father[y]=x;
    else{
        if(rank[x]==rank[y])
            rank[y]++;
        father[x]=y;
    }
    return true;
}
bool cmp(Edge a,Edge b)
{
    return a.weight<b.weight;
}
bool isfull(bool *flag,int n)//判斷所有的點都屬於一個集合了
{
    for(int i=0;i<n;++i){
        if(!flag[i])
            return false;
    }
    return true;
}
int main()
{
   // freopen("in.txt","r",stdin);
    Edge a[maxn];
    bool flag[maxn];
    int v,e;
    cin>>v>>e;
    memset(flag,false,sizeof(flag));
    for(int i=0;i<e;++i)
        cin>>a[i].begin>>a[i].end>>a[i].weight;
    int father[maxn],rank[maxn];
    for(int i=0;i<v;++i){
        father[i]=i;
        rank[i]=0;
    }
    sort(a,a+e,cmp);
    for(int i=0;i<v;++i){
        if(isfull(flag,v))
            break;
        Union(a[i].begin,a[i].end,father,rank);//每次將兩個集合進行合併
        cout<<"("<<a[i].begin<<","<<a[i].end<<")"<<endl;
        flag[a[i].begin]=true;
        flag[a[i].end]=true;
    }
    return 0;
}
發佈了113 篇原創文章 · 獲贊 27 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章