题目
方法一:Kruskal 算法
Kruskal 算法是一种常见并且好写的最小生成树算法。该算法的基本思想是从小到大加入边,是一个贪心算法。
其算法流程为:
- 假设连通图 G = ( V , E ) G=(V,E) G=(V,E),令最小生成树的初始状态为只有 n n n个顶点而无边的非连通图 T = ( V , { } ) T=(V,\{\}) T=(V,{}),图中每个顶点自成一个连通分量
- 在 E E E中选择代价最小的边,若该边依附的顶点分别在 T T T中不同的连通分量上,则将此边加入到 T T T中;否则,舍去此边而选择下一条代价最小的边
- 依此类推,直至 T T T中所有顶点构成一个连通分量为止
在本题中,首先将这张完全图中的边全部提取到边集数组中,然后对所有边进行排序,从小到大进行枚举,每次贪心选边加入答案。使用并查集维护连通性,若当前边两端不连通即可选择这条边。
class Solution {
public int minCostConnectPoints(int[][] points) {
int n = points.length;
UnionFind uf = new UnionFind(n);
// 边集数组
List<Edge> edges = new ArrayList<>();
for (int i = 0; i < n; i++) {
for (int j = i+1; j < n; j++) {
edges.add(new Edge(dist(points, i, j), i, j));
}
}
// 对所有边进行排序(从小到大)
Collections.sort(edges, new Comparator<Edge>() {
public int compare(Edge e1, Edge e2) {
return e1.len - e2.len;
}
});
int ret = 0;
int num = 1; // 树的边数 = n-1
// 从小到大进行枚举,每次贪心选边加入答案
for (Edge edge : edges) {
int len = edge.len, x = edge.x, y = edge.y;
if (uf.find(x) != uf.find(y)) {
uf.union(x, y);
ret += len;
num++;
if (num == n) {
break;
}
}
}
return ret;
}
public int dist(int[][] points, int x, int y) {
return Math.abs(points[x][0]-points[y][0])+Math.abs(points[x][1]-points[y][1]);
}
}
class Edge {
int len, x, y;
public Edge (int len, int x, int y) {
this.len = len;
this.x = x;
this.y = y;
}
}
class UnionFind {
int[] parent;
int[] rank;
int n;
public UnionFind(int size) {
parent = new int[size];
rank = new int[size];
Arrays.fill(rank, 1);
for (int i = 0; i < size; i++) {
parent[i] = i;
}
}
public int find(int x) {
if (x != parent[x]) {
parent[x] = find(parent[x]);
}
return parent[x];
}
public void union(int x, int y) {
int fx = find(x), fy = find(y);
if (fx == fy) {
return ;
}
if (rank[fx] < rank[fy]) {
int temp = fx;
fx = fy;
fy = temp;
}
rank[fx] += rank[fy];
parent[fy] = fx;
}
}
-
时间复杂度: O ( n 2 log ( n ) ) O(n^2\log(n)) O(n2log(n)),其中 n n n 是节点数。一般 K r u s k a l Kruskal Kruskal 是 O ( m log m ) O(m\log m) O(mlogm) 的算法,但本题中 m = n 2 m=n^2 m=n2,因此总时间复杂度为 O ( n 2 log ( n ) ) O(n^2\log(n)) O(n2log(n))
-
空间复杂度: O ( n 2 ) O(n^2) O(n2),其中 n n n 是节点数。并查集使用 O ( n ) O(n) O(n) 的空间,边集数组需要使用 O ( n 2 ) O(n^2) O(n2) 的空间。