Kruskal算法:基于贪心算法得到带权连通图的最小生成树的算法
最小生成树
生成树
- 包含无向带权图的所有顶点
- 边数量为顶点数减1
- 并且是连通的
最小生成树:无向连通带权图的连通图集合中,权值最小的生成树
如上图所示、右边的子树就是左边图的最小生成树
V = {A,B,C,D,E,F} // 生成树的顶点集合
E = {AB,BC,CD,CE,DF} // 生成树的边集合
W = 15 // 最小生成树的权重
Kruskal算法步骤
算法步骤
- 对所有边排序
- 依次访问当前最小边e
- 如果边e加入树T中,T不存在环,则把e加入T中;否则跳过
- 直到选择n-1条边加入T为止
详细过程举例
无向连通图G如下
首先对图G排序、从小到大依次为
访问当前最小边CD、因为无环加入子树T中:E={CD}
访问当前最小边AC、因为无环加入子树T中:E={CD,AC}
访问当前最小边AB、因为无环加入子树T中:E={CD,AC,AB}
访问当前最小边BC、会导致ABC形成环,所以跳过
访问当前最小边BE、因为无环加入子树T中:E={CD,AC,AB,BE}
访问当前最小边EF、因为无环加入子树T中:E={CD,AC,AB,BE,EF};
并且因为边数量num(E)=num(V)-1,所以已经找到最小生成树
Kruskal算法证明
证明Kruskal算法分为两个步骤
- Kruskal算法生成的树是生成树
- Kruskal算法的生成树的权值是所有子树里最小的
对于图G(V,E,W),num(V)=n,Kruskal算法得到的树为T
T是生成树
先证明T是连通子图:假设T的顶点不是连通的,
- 那么由于T不存在环(Kruskal算法要求)、T是有多个连通子图构成即:T={T1、T2......}。
- 那么T边的数量:
num(T) = num(T1)+num(T2)+...... = num(V1)-1 + num(V2)-1 + ......所以num(T)小于n-1,这与num(T)=n-1矛盾! - 所以T是连通子图、由于没有环、T的顶点数肯定为n,所以T是生成树
T的权值最小
证明之前需要了解生成树的两个性质
-
加入任意一条不是生成树的边都会形成环
-
加入任意一条不是生成树的边形成环后,删除环的任意一条边都可以形成一个新的生成树
对于图G(V,E,W):令图G最小生成树为T1,Kruskal算法生成的数为T
- 假设在Kruskal算法过程中遇到第一条不在T1的边为e:e∈E(T)、e∉E(T1)
- 根据生成树的性质在T1中加入e,必然会生成一个环C;并且由于T中不存在环,所以C中必有一条边f∉T,f∈T1
- 根据生成树的性质T1+e-f的树也是生成树、并且W(T1+e-f)>=W(T1),得到W(e)>=W(f)
- 所以在Kruskal算法过程中遇到e时,我们同样可以选择W(f);依次类推最后T会和T1完全相同
- 所以证明T也是最小生成树
JS实现(邻接矩阵)
/**
* 获取链表尾部结点
* @param nextArray
* @param start
* @return {*}
*/
function findEndNode (nextArra```
对于图G(V,E,W):令图G最小生成树为T1,Kruskal算法生成的数为T
```y, start) {
while (nextArray[start] !== null) {
start = nextArray[start];
}
return start;
}
/**
* kruskal算法
* @param matrix
* @return {*[{i,j,weight}]}
*/
function kruskal (matrix) {
let edgeList = [];
for (let i = 0; i < matrix.length; i++) {
for (let j = 0; j < matrix.length; j++) {
let weight = matrix[i][j];
edgeList.push({i, j, weight});
}
}
// 所有边从小到大排列
edgeList.sort((a, b) => {
return a.weight - b.weight;
})
// 记录树T的所有边
let res = [];
// 子树链表结构的下一个结点
let nextArray = [];
nextArray.length = matrix.length;
nextArray.fill(null);
while (res.length < matrix.length - 1) {
let edge = edgeList.shift();
let u = findEndNode(nextArray, edge.i);
let v = findEndNode(nextArray, edge.j);
if (u !== v) {
res.push(edge);
nextArray[u] = v;
}
}
return res;
}
PS
以上步骤截图都来自小程序:数据结构算法演示。小程序包含常用的数据结构算法演示、欢迎大家使用