Kruskal演算法是以邊為主導地位,始終都是選擇當前的可用的最小權值的邊。
其主要思想為
1:設一個n個頂點的連通網路為G(V,E),最初先構造一個只有n個頂點沒有邊的非連通圖T=(V,空集),每個頂點自成一格連通分量。
2:當在E中選擇一條具有最小權值的邊時,若該邊的兩個頂點落在不同的連通分量上就將此邊加入T中,否則就捨去並且以後永久的不使用這條邊,重新選擇一條權值最小的邊。
3:如此重複,直到所有的頂點在同一個連通分量上。
Kruskal演算法在沒選擇一條邊加入到產生樹的集合T中時有兩個關鍵步驟如下:
1:從E中選擇當前權值最小的邊,實現時可以用最小堆來存放E中的所有的邊或者使用結構體將所有邊的資訊(邊的兩個頂點和權值)存放在一個結構體類型的Edge數組中,並按權值的大小從小到大排序,之後按順序選用每條邊
2:選擇最小權值的邊後,要判斷兩個頂點是否屬於同一個連通分量上,如果是則捨去,如果不是則選用並將這兩個頂點分別所在的連通分量合并成一個連通分量。在實現時可以使用並查集來判斷兩個頂點是否屬於同一個連通分量以及將兩個連通分量合并成一個連通分量。
接下來介紹並查集:
並查集就是用來解決兩個元素是否屬於同一個集合,以及把兩個集合合并成一個集合
並查集在處理時有搜尋和合并兩個運算,為了實現並查集的描述和實現,通常把先後加入到集合中的元素表示成一個樹結構,並用根節點的序號來代表這個集合,為此定義了一個數組parent[],例如parent[4]=5,意思是4號節點的父親是5號節點,而parent[7]=-4的意思是7號節點該集合的根節點並且這個集合有4個元素
實現並查集主要有3個函數,代碼如下:
一:
void UFset()//初始化{for(i=1;i<=n;i++)parent[i]=-1;}
二:
int Find(int x)//尋找並返回節點x所屬集合的根節點{int s;//尋找位置for(s=x;parent[s]>=0;s=parent[s]);while(s!=x)//最佳化方案...壓縮路徑,使後續的尋找操作加速{int tmp=parent[x];parent[x]=s;x=tmp;}return s;}
所謂的壓縮路徑就是增加一個while迴圈,每次把從節點x到集合的根節點的路徑上的結點直接設定為根節點的子女節點。雖然增加了時間,但後續的尋找會更快
三:
//將兩個不同的集合的元素進行合并,使兩個集合中任意的兩個元素都連通
void Union(int R1,int R2){int r1=Find(R1),r2=Find(R2);//r1為R1的根節點,r2為R2的根節點int tmp=parent[r1]+parent[r2];//兩個集合的結點個數之和(為負數)//如果R2所在的樹結點的個數》R1所在的樹結點的個數(注意parent[r1]是負數)if(parent[r1]>parent[r2])//最佳化方案.....加權法則{parent[r1]=r2;//將r1所在的樹作為r2的子樹parent[r2]=tmp;//更新r2的parent[]的值}else{parent[r2]=r1;//將r2所在的樹的作為r1的子樹parent[r1]=tmp;//更新r1的parent[]的值}}
接下來就是Kruskal演算法了 主要代碼如下:
void Kruskal(){int sumweight=0;//產生樹的權值int num=0;//已選用的邊的數目int u,v;UFset();//初始化parent【】for(i=0;i<m;i++){u=edges[i].u;v=edges[i].v;if(Find(u)!=Find(v))//判斷是否屬於同一連通分量{cout<<u<<' '<<v<<' '<<edges[i].w<<endl;sumweight+=edges[i].w;num++;Union(u,v);//合并兩個集合}if(num>=n-1)break;}cout<<"Weight of MST is "<<sumweight<<endl;}
完美補充:
一:
邊的表示
struct edge{//邊
int u,v,w;//邊的頂點,權值
}Edges[MAXM];
二:邊的權值按小到大排序使用sort函數
int cmp(edge a,edge b)
{
return a.w<b.w;
}
模版一般,如有更好的,不吝賜教