Kruskal算法
Kruskal算法是一种用来查找最小生成树的算法,由Joseph Kruskal在1956年发表。用来解决同样问题的还有Prim算法和Boruvka算法等。三种算法都是贪心算法的应用。
概念解释
Kruskal算法定义:**先构造一个只含 n 个顶点、而边集为空的子图,把子图中各个顶点看成各棵树上的根结点,之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,即把两棵树合成一棵树,反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直到森林中只有一棵树,也即子图中含有 n-1 条边为止。
最小生成树定义:一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。(可以由Kruskal算法或者prim算法求出来)
执行步骤::
第一步:在带权连通图中,将边的权值排序;
第二步:判断是否需要选择这条边(此时图中的边已按权值从小到大排好序)。判断的依据是边的两个顶点是否已连通,如果连通则继续下一条;如果不连通,那么就选择使其连通。
第三步:循环第二步,直到图中所有的顶点都在同一个连通分量中,即得到最小生成树。
图解Kruskal算法
![[外链图片转存失败(img-SMyVUx2V-1562946282066)(img/1.png)]](https://code84.com/wp-content/uploads/2022/10/20190712234528549.png)
首先将图中的所有的边按权值从小到大排序:
HG < (CI=GF) < (AB=CF) < GI < (CD=HI) < (AH=BC) < DE < BH < DF
接着,我们选择HG这条边,这样就将H和G这两个点加入到了已经找到的点的集合。如下图所示:
![[外链图片转存失败(img-uZ2FLyHQ-1562946282068)(img/3.png)]](https://code84.com/wp-content/uploads/2022/10/20190712234615858.png)
由于两个一样的权值的边存在,所以就可以随便选一条,但是需要做出判断。
判断的法则:
(1)当所选的边加入已找到边的集合时候,会不会形成回路。如果没有形成回路,那么就“准备”将其连通,在真正连通之前还需要做一次判断,判断这两个点是否已经加入到已找到点的集合中去了
(2)如果两个点都没有出现,则将这两个点都加入到集合中去
(3)如果其中有一个点已经出现在集合中,则将另一个没有出现过的点,加入到集合中去
(4)如果两个都有出现,则不需要加入。
![[外链图片转存失败(img-RpZmzgTZ-1562946282070)(img/2.png)]](https://code84.com/wp-content/uploads/2022/10/20190712234634399.png)
![[外链图片转存失败(img-D3tpD4SM-1562946282074)(img/4.png)]](https://code84.com/wp-content/uploads/2022/10/20190712234645422.png)
![[外链图片转存失败(img-YpGFm3bz-1562946282076)(img/5.png)]](https://code84.com/wp-content/uploads/2022/10/2019071223465759.png)
以上三步不会形成回路,所以可以直接连通。
![[外链图片转存失败(img-c8DwVRU9-1562946282079)(img/6.png)]](https://code84.com/wp-content/uploads/2022/10/201907122347245.png)
在连接IG的时候,会形成回路,所以放弃连通。
如何判断的?注意听这里是算法实现的重点:
判断两个已经连接到第三个点的点,会不会连接。
![[外链图片转存失败(img-L9NUaVwQ-1562946282080)(img/7.png)]](https://code84.com/wp-content/uploads/2022/10/20190712234735125.png)
![[外链图片转存失败(img-7HVtpbrO-1562946282095)(img/8.png)]](https://code84.com/wp-content/uploads/2022/10/2019071223474692.png)
至此我们得到了一个最小生成树。所有的点都在一个连通分量上了。
注意点:排序和防止回路
排序的解决:很好解决
(1)、只能用图中有的边;
(2)、只能用掉|v|-1条边;
(3)、不能有回路。
kruskal和prim的比较:
kruskal适用于比较稀疏的图,因为他每次都是从最小的边开始查找。让森林合并为树
prim算法适用于变数比较多的图。让小树慢慢长大。
简单的例子:
![[外链图片转存失败(img-68olnHfZ-1562946282096)(img/9.png)]](https://code84.com/wp-content/uploads/2022/10/20190712234819727.png)
public class Kruskal2 {
private Edge[] edges;
private int edgeSize;
public Kruskal2(int edgeSize) {
this.edgeSize = edgeSize;
edges = new Edge[edgeSize];
createEdgeKruskal();
}
/**
* 创建边的集合,从小到大
*/
private void createEdgeKruskal() {
// Edge edge0 = new Edge(6, 7, 1);
// Edge edge1 = new Edge(5, 6, 2);
// Edge edge2 = new Edge(2, 8, 2);
// Edge edge3 = new Edge(0, 1, 4);
// Edge edge4 = new Edge(2, 5, 4);
// Edge edge5 = new Edge(6, 8, 6);
// Edge edge6 = new Edge(7, 8, 7);
// Edge edge7 = new Edge(2, 3, 7);
// Edge edge8 = new Edge(1, 2, 8);
// Edge edge9 = new Edge(0,7 , 8);
// Edge edge10 = new Edge(3, 4, 9);
// Edge edge11 = new Edge(4, 5, 10);
// Edge edge12 = new Edge(1, 7, 11);
// Edge edge13 = new Edge(3, 5, 14);
// Edge edge14 = new Edge(4, 5, 26);
Edge edge0 = new Edge(0, 3, 1);
Edge edge1 = new Edge(1, 2, 2);
Edge edge2 = new Edge(0,1,3);
Edge edge3 = new Edge(1, 3, 5);
Edge edge4 = new Edge(2, 3, 8);
edges[0] = edge0;
edges[1] = edge1;
edges[2] = edge2;
edges[3] = edge3;
edges[4] = edge4;
// edges[5] = edge5;
// edges[6] = edge6;
// edges[7] = edge7;
// edges[8] = edge8;
// edges[9] = edge9;
// edges[10] = edge10;
// edges[11] = edge11;
// edges[12] = edge12;
// edges[13] = edge13;
// edges[14] = edge14;
}
/**
* kruskal算法创建最小生成树
*/
public void createMinSpanTreeKruskal() {
// 定义一个一维数组,下标为连线的起点,值为连线的终点
int[] parent = new int[edgeSize];
for (int i = 0; i < edgeSize; i++) {
parent[i] = 0;
}
int sum = 0;
for (Edge edge : edges) {//6, 7, 1
// 找到起点和终点在临时连线数组中的最后连接点
int start = find(parent, edge.start);// 6 2
int end = find(parent, edge.end); // 7 8
// 通过起点和终点找到的最后连接点是否为同一个点,是则产生回环
if (start != end) {
// 没有产生回环则将临时数组中,起点为下标,终点为值
parent[start] = end;
System.out.println("访问到了节点:{" + start + "," + end + "},权值:" + edge.weight);
sum += edge.weight;
}
}
for (int i : parent) {
System.out.println(i+",");
}
System.out.println("最小生成树的权值总和:" + sum);
}
/**
* 获取集合的最后节点
*/
private int find(int parent[], int index) {
// System.out.println(parent[index]);
while (parent[index] > 0) {
index = parent[index];
}
return index;
}
/**
* 连接顶点的边
*/
class Edge {
private int start;
private int end;
private int weight;
public Edge(int start, int end, int weight) {
this.start = start;
this.end = end;
this.weight = weight;
}
}
public static void main(String[] args) {
Kruskal2 graphKruskal = new Kruskal2(5);
graphKruskal.createMinSpanTreeKruskal();
}
}
版权声明:本文为new_buff_007原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。