图的生成树:一个联通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。
最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。
1.Kruskal算法
主要思想:初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。
具体步::
1.把图中的所有边按代价从小到大排序;
2.把图中的n个顶点看成独立的n棵树组成的森林;
3.按权值从小到大选择边,所选的边连接的两个顶点ui,vi应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树,ui,vi属如果于相同的树则该边丢弃。
4.重复(3),直到所有顶点都在一颗树内或者有n-1条边为止。
1584. 连接所有点的最小费用利用Union-Find 算法实现Kruskal
class Solution {
public int minCostConnectPoints(int[][] points) {
int n=points.length;
//生成边及权重
List<int[]> edges=new ArrayList<>();
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
int xi=points[i][0],yi=points[i][1];
int xj=points[j][0],yj=points[j][1];
edges.add(new int[]{
i,j,Math.abs(xi-xj)+Math.abs(yi-yj)
});
}
}
Collections.sort(edges,(a,b)->{
return a[2]-b[2];
});
int mst=0;
UF uf=new UF(n);
for(int[] edge:edges){
int u=edge[0];
int v=edge[1];
int weight=edge[2];
if(uf.connected(u,v)){
continue;
}
mst+=weight;
uf.union(u,v);
}
return mst;
}
class UF {
// 连通分量个数
private int count;
// 存储一棵树
private int[] parent;
// 记录树的「重量」
private int[] size;
// n 为图中节点的个数
public UF(int n) {
this.count = n;
parent = new int[n];
size = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
size[i] = 1;
}
}
// 将节点 p 和节点 q 连通
public void union(int p, int q) {
int rootP = find(p);
int rootQ = find(q);
if (rootP == rootQ)
return;
// 小树接到大树下面,较平衡
if (size[rootP] > size[rootQ]) {
parent[rootQ] = rootP;
size[rootP] += size[rootQ];
} else {
parent[rootP] = rootQ;
size[rootQ] += size[rootP];
}
// 两个连通分量合并成一个连通分量
count--;
}
// 判断节点 p 和节点 q 是否连通
public boolean connected(int p, int q) {
int rootP = find(p);
int rootQ = find(q);
return rootP == rootQ;
}
// 返回节点 x 的连通分量根节点
private int find(int x) {
while (parent[x] != x) {
// 进行路径压缩
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
}
// 返回图中的连通分量个数
public int count() {
return count;
}
}
}
2.Prim算法
主要思想:每次迭代选择代价最小的边对应的点,加入到最小生成树中。不同的是算法从某一个顶点s开始,逐渐长大覆盖整个连通网的所有顶点。
1.图的所有顶点集合为V;初始令集合u=[s],v=V-u;
2.在两个集合u.v能够组成的边中,选择一条代价最小的边(u0,v0),加入到最小生成树中,并把v0并入到集合u中。 3.重复上述步骤,直到最小生成树有n-1条边或者n个顶点为止。
由于不断向集合u中加点,所以最小代价边必须同步更新;需要建立一个辅助数组closedge,用来维护集合v中每个顶点与集合u中最小代价边信息。