引入
最近开始学习Golang了,所以以后的Leetcode题解都用Golang来写。(ps:Golang没有基础的数据结构,如List、Queue、Stack、PriorityQueue等等,所以这个想法可以放弃掉了)
上周周赛有这么一道题1584. 连接所有点的最小费用,以前没有遇到过最小生成树的题,并且本题还是稠密图(即一个节点到各个节点都是联通的,反之如果联通的节点树木比较少,称为稀疏图),本题具有代表意义。
图的节点之间是相互联通的,联通的距离为曼哈顿距离,需要返回一个“连接所有点的最小总距离”,即构建一个最小生成树。
构建方式一:Prim算法
- 输入:一个加权连通图,其中节点集合为 V V V,边集合为 E E E;
- 初始化: V c u r r = { x } V_{curr} = \{x\} Vcurr={x},其中x为集合V中的任一节点(起始点), E c u r r = { } E_{curr} = \{\} Ecurr={},即初始化边为空;
- 过程:
- 在边集合 E E E中选取距离最小的边 < x , y > <x,y> <x,y>,其中 x x x为集合 V c u r r V_{curr} Vcurr中的元素,而 y y y不在集合里;
- 将 y y y加入到集合 V c u r r V_{curr} Vcurr中,将 < x , y > <x,y> <x,y>加入到集合 E c u r r E_{curr} Ecurr中;
- 重复步骤1,2直到 V c u r r = V V_{curr}=V Vcurr=V。
很容易理解,不做赘述。
构建方式二:Kruskal算法
- 输入:一个加权连通图 G G G,其中节点集合为 V V V,边集合为 E E E;
- 初始化:
- 新建一个图 G n e w G_{new} Gnew,该图具备与原图中相同的节点集合 V V V,但没有边;
- 将原图 G G G的e个边按权值大小从小到大排序。
- 过程:从权值最小的边开始遍历每条边,如果这条边上的两个节点 < x , y > <x,y> <x,y>在 G n e w G_{new} Gnew中不在一个联通分量中,那么将这条边添加到图 G n e w 中 G_{new}中 Gnew中
过程里关键词其实是联通分量,怎么理解这个联通分量呢?可以简单的理解为集合: 在新的图 G n e w G_{new} Gnew上能够互通的两个节点,处于同一个联通分量上 。
参考:最小生成树算法
本题题解
在知晓这两个算法之后,如何解呢?
在稠密图的情况下,Prim算法会有更好的性能,因为用Kruskal要记录的边太多了,有 n ( n − 1 ) n(n-1) n(n−1)个。
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Set;
public class Solution {
public int minCostConnectPoints(int[][] points) {
int Len = points.length;
int[][] distances = new int[Len][Len];
//计算曼哈顿距离
for (int i = 0; i < Len; i++) {
int[] p1 = points[i];
for (int j = 0; j < i; j++) {
int[] p2 = points[j];
distances[i][j] = Math.abs(p1[0] - p2[0]) + Math.abs(p1[1] - p2[1]);
distances[j][i] = distances[i][j];
}
}
//PRIM算法
Set<Integer> set = new HashSet<>();//标记Gnew已经加入了哪些点
PriorityQueue<Dis> priorityQueue = new PriorityQueue<>((o1, o2) -> o1.distance - o2.distance);
//加入初始点
set.add(0);
for (int i = 1; i < Len; i++) {
priorityQueue.add(new Dis().addDistance(distances[0][i]).addPointer(0, i));
}
int sum = 0;
//Prim算法开始
while (set.size() < Len) {
//找出priorityQueue里面最小的
Dis curr = priorityQueue.poll();
if (set.contains(curr.pointers[0]) && set.contains(curr.pointers[1])) {
continue;
} else {
sum += curr.distance;
if (!set.contains(curr.pointers[0])) {
set.add(curr.pointers[0]);
for (int i = 0; i < Len; i++) {
if (i != curr.pointers[0]) {
priorityQueue.add(new Dis()
.addDistance(distances[i][curr.pointers[0]])
.addPointer(i, curr.pointers[0]));
}
}
} else {
set.add(curr.pointers[1]);
for (int i = 0; i < Len; i++) {
if (i != curr.pointers[1]) {
priorityQueue.add(new Dis()
.addDistance(distances[i][curr.pointers[1]])
.addPointer(i, curr.pointers[1]));
}
}
}
}
}
return sum;
}
class Dis {
public int distance;
public int[] pointers = new int[2];
Dis addDistance(int distance) {
this.distance = distance;
return this;
}
Dis addPointer(int a, int b) {
pointers[0] = a;
pointers[1] = b;
return this;
}
}
}