1. kruskal算法同样是解决无向图中最小生成树问题,与Prim算法不同的是,kruskal算法采用了边贪心的策略,其思想及其简洁,理解难度要比Prim算法要低很多,算法的基本思想如下:
在初始状态的时候隐去图中所有的边,这样图中每个顶点都自成一个联通块,之后执行下面的步骤:
① 对所有的边按照边权从小到大进行排序
② 按照边权从小到大测试所有边,如果当前测试边所连接的两个顶点不在一个联通块中,则把这条测试边加入到当前的生成树中,否则将其舍弃,因为假如当前的边之后会构成连通图了
③ 执行步骤②,直到生成树中的边数等于总的顶点数减1或者是所有的边测试结束的时候,而当结束的时候如果最小生成树的边数小于总的顶点数减1说明该图是不连通的
对于上面的算法描述我们需要解决两个问题:
1)怎么样检测测试边所连接的两个顶点是否在同一个联通块中,如果把连通块当成一个集合,那么我们只需要检查两个端点是否在同一个集合中,所以可以并查集数据结构来帮助我们解决
2)如果将测试边加入到最小生成树中,并查集的合并功能就可以帮助我们将不在一个集合中的顶点加入到一个集合中这样就帮助我们完成了加入到最小生成树中需求
2. 下面采用的是Java语言来完成编程的过程
① 首先需要解决的是怎么样存储图的问题,因为我们是需要按照边权对边从小到大进行排序,而且需要知道当前边对应的起始顶点和结束顶点,所以不能够使用二维数组来存储图,因为二维数组存储之后不能够对其进行排序,所以我们需要想另外一个方法就是Java语言的一个特点就是封装,可以将数组封装到一个类中,所以我们可以将边的起始顶点、结束顶点和边的权重封装到一个类中,然后声明一个对象数组,数组的长度是图中边的数目,这样我们在接收到输入数据的时候可以直接将数据存入到对象数组中(对象数组中的每一个元素都是一个对象)
② 接下来就需要对对象数组进行排序,对于对象数组的排序,我们需要创建一个Comparator对象里面实现自定义的比较方法,按照边权对数组进行排序,在方法中只需要传入对象数组和Comparator比较器对象即可实现对象数组的排序
③ 循环遍历排序后的数组,依次检查每一条边,其中使用到了并查集数据结构,在查询当前节点的根节点的时候将当前节点到根节点路径上的所有节点的父节点都指向了根节点
④ 使用一个变量来记录当前生成树的边数,当发现等于顶点数减1的时候name最小生成树就构造完成了
3. 下面是一个无向有权图的例子(最小生成树为图中标出来棕色部分):
最终最小生成树的总的权重为11
下面是测试数据:
6
10
0 1 4
0 4 1
0 5 2
4 5 3
1 5 3
3 5 4
3 4 5
2 5 5
2 3 6
1 2 1
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
public class Main {
static int n;
static int edgeNum;
static Edge[] edges;
static int count = 0;
static int father[];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("输入顶点数: ");
n = sc.nextInt();
father = new int[n];
for(int i = 0; i < n; i++){
father[i] = i;
}
System.out.println("输入边数: ");
edgeNum = sc.nextInt();
edges = new Edge[edgeNum];
for(int i = 0; i < edges.length; i++){
edges[i] = new Edge();
}
System.out.println("输入起始顶点, 就是顶点和边的权重: ");
for(int i = 0; i < edgeNum; i++){
edges[i].u = sc.nextInt();
edges[i].v = sc.nextInt();
edges[i].cost = sc.nextInt();
}
Comparator<Edge> comparator = new Comparator<Main.Edge>() {
@Override
public int compare(Edge o1, Edge o2) {
if(o1.cost > o2.cost) return 1;
if(o1.cost < o2.cost) return -1;
return 0;
}
};
Arrays.sort(edges, comparator);
int res = kruskal();
System.out.println(res);
sc.close();
}
private static int kruskal() {
int ans = 0;
for(int i = 0; i < edgeNum; i++){
int faU = findFather(edges[i].u);
int faV = findFather(edges[i].v);
if(faU != faV){
father[faU] = faV;
ans += edges[i].cost;
count++;
if(count == n - 1) break;
}
}
return ans;
}
private static int findFather(int u) {
if(u == father[u]) return u;
int F = findFather(father[u]);
father[u] = F;
return F;
}
//使用一个类来封装起始顶, 结束顶点,边的权重
public static class Edge{
int u;
int v;
int cost;
public int getU() {
return u;
}
public void setU(int u) {
this.u = u;
}
public int getV() {
return v;
}
public void setV(int v) {
this.v = v;
}
public int getCost() {
return cost;
}
public void setCost(int cost) {
this.cost = cost;
}
@Override
public String toString() {
return "Edge [u=" + u + ", v=" + v + ", cost=" + cost + "]";
}
}
}