介绍
克鲁斯卡尔(Kruskal)算法是用来求出连通图中最小生成树的算法。
连通图:指==无向图==中==任意两点都能相通==的图。
最小生成树:指联通图的所有生成树中==边权重的总和最小==的树(即,找出一个树,让其联通所有的点,并让树的边权和为最小)。
算法思想
克鲁斯卡尔算法的主要基本思想有两点原则:
- 按照从小到大的顺序选择边,并将边的两端连线,构成新的图
- 保证新加入的边不能在新的图上形成环
- 重复以上步骤,直至添加
n-1
条边
用图表示该算法的解体过程:
算法证明
我是通过反证的方式理解该算法的。
证明按上述算法添加
n-1
条边时,一定能连通n
个节点。证明新的图中再添加一条边,一定构成环。
证明在构成新的环中,新加入的边一定是最长的边。
算法实现
public class Kruskal {
public static void generateMinTree(int[][] graph){
if(graph == null || graph.length <=0)
throw new IllegalArgumentException();
int minSum = 0;
//标记哪些点已经到访过
int[][] visited = new int[graph.length][graph.length];
//用来表示父子级的关系,验证是否存在环
int[] nodeHierarchy = new int[graph.length];
for(int i=0; i<nodeHierarchy.length; i++){
nodeHierarchy[i] = i;
}
int n = 0;
while(n < graph.length -1){
int minVal = Integer.MAX_VALUE;
int iIndex = 0;
int jIndex = 0;
for(int i=0; i<graph.length; i++){
for(int j=i+1; j<graph[i].length; j++){
if(graph[i][j] != Integer.MAX_VALUE && visited[i][j] == 0 && graph[i][j] < minVal){
iIndex = i;
jIndex = j;
minVal = graph[i][j];
}
}
}
visited[iIndex][jIndex] = 1;
//判断父节点是否相同,确定是否构成了环
if(findFather(nodeHierarchy, iIndex) != findFather(nodeHierarchy, jIndex)){
System.out.println(n + " Round min value path: " + minVal + " from " + iIndex + " to " + jIndex);
minSum += graph[iIndex][jIndex];
updateHierarchy(nodeHierarchy, iIndex, jIndex);
n++;
}
System.out.println("node hierarchy:" + Arrays.toString(nodeHierarchy));
}
System.out.println("min tree path sum:" + minSum);
System.out.println("node hierarchy:" + Arrays.toString(nodeHierarchy));
}
//递归查找父节点
private static int findFather(int[] nodeHierarchy, int idx){
if(nodeHierarchy[idx] == idx)
return idx;
return findFather(nodeHierarchy, nodeHierarchy[idx]);
}
//递归更新父节点
private static void updateHierarchy(int[] nodeHierarchy, int from, int to){
if(nodeHierarchy[from] != from)
updateHierarchy(nodeHierarchy, nodeHierarchy[from], from);
nodeHierarchy[from] = to;
}
}
上述代码见Github。