Kruskal演算法實現最小生成樹MST(java)
阿新 • • 發佈:2019-02-15
Kruskal演算法用於生成圖的最小生成樹MST,不多說下面直接進入主題!
一、實現Kruskal演算法需要會的資料結構知識
1、最小堆:包括最小堆的初始化、插入和刪除操作
最小堆的作用:每次從邊的集合中選出權重最小的邊,將其加入到MST中(當然此邊當和MST中的元素構成環時不滿足)
2、等價類(並查集)
並查集的作用:便是判斷加入到MST中的邊是否會構成環
3、EdgeNode類
在Kruskal演算法中,圖是加權圖,為了更好地對邊進行操作,我們需要定義一個EdgeNode類來對圖中的加權邊進行操作
只有弄明白上面的資料結構知識,下面才能夠比較看得明白!
二、Kruskal演算法實現的具體思路
首先給出虛擬碼:
其實Kruskal演算法的思想很簡單,是典型的貪婪演算法
1、首先我們利用並查集的思想將圖中的所有頂點初始化成一棵樹,每個頂點物件包含兩個域:parent域和root域
2、按照每條邊權重的大小對所有未加入MST中的邊進行排序,利用最小堆!演算法複雜度小!
3、每次選出權重最小的邊加入到MST中,利用並查集的思想,判斷兩個頂點是否在一個樹中,也就是是否會構成環!
4、當所有的邊操作完後便形成了我們需要的最小生成樹MST
三、Kruskal演算法java具體實現原始碼
1、EdgeNode類
public class EdgeNode {
int weight;
int u,v;
}
2、Kruskal演算法實現類
import java.util.ArrayList;
import java.util.Scanner;
/**
* <pre>
* 使用kruskal演算法生成圖的MST
* 其中:
* 1>.圖的頂點存在了陣列中,存在是int
* 2>.圖的帶權邊使用了EdgeNode物件,存在了陣列中
* 3>.邊按照權重進行排序利用最小堆進行排序,每次取出最小堆的根節點,便是權最小的邊
* 4>.每次向MST中新增邊肯定新增最小權的邊,唯一條件便是不構成環
* 5>.上述不構成環是利用等價類<並查集>實現的
* 6>.並查集實現:樹!每個頂點兩個域:parent域&root域!find-union!重量規則!</pre>
* @author King
*
*/
public class Kruskal {
boolean[] root;
int[] parent;
int currentSize=0;
int maxSize=0;
EdgeNode[] minHeap=new EdgeNode[20];
/**
* 初始化每個頂點為一個類
* @param verNum 頂點的數量
*/
public void initialize(int verNum){
root=new boolean[verNum+1];
parent=new int[verNum+1];
for(int vertex=1;vertex<=verNum;vertex++){
parent[vertex]=1;
root[vertex]=true;
}
}
/**
* 尋找某個頂點元素所在的類
* @param vertex 頂點
* @return 返回的是頂點所在的類
*/
public int find(int vertex){
while(!root[vertex]){
vertex=parent[vertex];
}
return vertex;
}
/**
* 利用重量規則將兩個根節點為i,j的類合併
* @param i 根節點為i
* @param j 根節點為j
*/
public void union(int i,int j){
if(parent[i]<parent[j]){
parent[j]+=parent[i];
root[i]=false;
parent[i]=j;
}else{
parent[i]+=parent[j];
root[j]=false;
parent[j]=i;
}
}
/**
* 通過weight構建以EdgeNode為節點的最小堆
* @param edgeNode為帶權的邊集
*/
public void createMinHeap(EdgeNode[] edgeNode){
currentSize=edgeNode.length;
maxSize=minHeap.length;
if(currentSize>=maxSize){
maxSize*=2;
minHeap=new EdgeNode[maxSize];
}
for(int i=0;i<currentSize;i++)
minHeap[i+1]=edgeNode[i];
int y,c;
for(int i=currentSize/2;i>=1;i--){
EdgeNode node=minHeap[i];
y=node.weight;
c=2*i;
while(c<currentSize){
if(c<=currentSize && minHeap[c].weight>minHeap[c+1].weight)
c++;
if(minHeap[c].weight>=y)
break;
minHeap[c/2]=minHeap[c];
c=c*2;
}
minHeap[c/2]=node;
}
}
/**
* 最小堆刪除兩種思路,一種和前面一樣,就是一直跟蹤放在根節點的那個最後一個節點最終插入的位置
* 另一種思路便是每一次完成完整的交換然後下一一層在進行同樣處理
*/
public EdgeNode deleteMinHeap(){
if(currentSize<1)
System.out.println("堆已經為空!無法執行刪除");
EdgeNode node=minHeap[1];
minHeap[1]=minHeap[currentSize];
currentSize-=1;
int c=2,j=1;
EdgeNode node1=minHeap[currentSize+1];
while(c<=currentSize){
if(c<currentSize && minHeap[c].weight>minHeap[c+1].weight)
c++;
if(node1.weight<=minHeap[c].weight)
break;
minHeap[j]=minHeap[c];
j=c;
c=c*2;
}
minHeap[j]=node1;
return node;
}
/**
* 根據圖的頂點集合帶權邊集生成MST
* @param verArray 頂點集
* @param edgeNode 帶權邊集
*/
public void minSpanningTree(int[] verArray,EdgeNode[] edgeNode){
ArrayList<EdgeNode> nodeList=new ArrayList<EdgeNode>();
initialize(verArray.length);
createMinHeap(edgeNode);
for(int i=1;i<=currentSize;i++){
System.out.println(minHeap[i].u+" "+minHeap[i].v+" "+minHeap[i].weight);
}
for(int i=0;i<edgeNode.length;i++){
EdgeNode node=deleteMinHeap();
int jRoot=find(node.u);
int kRoot=find(node.v);
if(jRoot!=kRoot){
nodeList.add(node);
union(jRoot,kRoot);
}
}
System.out.println("使用Kruskal演算法得到圖的最小生成樹為:");
for(int i=0;i<nodeList.size();i++){
System.out.println(nodeList.get(i).u+" "+nodeList.get(i).v+" "+nodeList.get(i).weight);
}
}
public static void main(String[] args) {
System.out.println("請輸出圖的頂點數和邊數:");
@SuppressWarnings("resource")
Scanner scan=new Scanner(System.in);
int verNum=scan.nextInt();
int edgeNum=scan.nextInt();
int[] verArray=new int[verNum];
System.out.println("請依次輸入頂點:");
for(int i=0;i<verNum;i++){
int vertex=scan.nextInt();
verArray[i]=vertex;
}
EdgeNode[] edgeNode=new EdgeNode[edgeNum];
System.out.println("請依次輸入邊的頂點和權重:");
for(int i=0;i<edgeNum;i++){
int u=scan.nextInt();
int v=scan.nextInt();
int weight=scan.nextInt();
EdgeNode node=new EdgeNode();
node.u=u;
node.v=v;
node.weight=weight;
edgeNode[i]=node;
}
Kruskal kruskal=new Kruskal();
kruskal.minSpanningTree(verArray,edgeNode);
}
}
測試所用的加權無向圖如下所示:
測試輸入資料格式截圖(部分):
測試結果為: