1584. 连接所有点的最小费用
思路一:Kruskal算法 [PriorityQueque + UnionFind]
思路:
- 将所有的边一次性全部读入并存储到PriorityQueue中,权重小的在priority queue上方。
- 将pq中的元素一一poll出来,这里使用UnionFind的方式,如果发现有环,就不添加这条边。
- 重复做2,直到所有unionfind中只存在一个类别。
class Solution {
public int minCostConnectPoints(int[][] points) {
int pointNum = points.length;
// 第一步:初始化Edge的PriorityQueue
PriorityQueue<Edge> pq = new PriorityQueue<>();
for(int i=0;i<pointNum;i++)
for(int j=i+1;j<pointNum;j++)
pq.add(new Edge(i,j,distanceManhatton(points[i],points[j])));
// 第二步:Kruskal算法,用UnionFind查找。
UnionFind uf = new UnionFind(pointNum);
int res = 0;
while(uf.count()>1){
Edge tmp = pq.poll();
if(uf.find(tmp.a)!=uf.find(tmp.b)){
uf.union(tmp.a,tmp.b);
res += tmp.value;
}
}
return res;
}
public int distanceManhatton(int[] point1,int[] point2){
int res = 0;
for(int i=0;i<point1.length;i++)
res+=Math.abs(point1[i]-point2[i]);
return res;
}
}
class Edge implements Comparable<Edge>{
int a,b; // 一条边的两个顶点编号
int value;
public Edge(int a,int b, int value){
this.a = a;
this.b = b;
this.value = value;
}
@Override
public int compareTo(Edge other) {
return this.value-other.value;
}
}
class UnionFind{
int[] elements;
int[] rank;
int types;
public UnionFind(int N){
this.rank = new int[N];
this.elements = new int[N];
this.types = N;
for(int i=0;i<N;i++)
this.elements[i]=i;
}
public int find(int i){
if(elements[i]==i)
return i;
else{
elements[i] = find(elements[i]);
return elements[i];
}
}
public void union(int i,int j){
int parenti = find(i);
int parentj = find(j);
if(rank[parenti]<rank[parentj])
elements[parenti]=parentj;
else if(rank[parenti]>rank[parentj])
elements[parentj]=parenti;
else{
elements[parentj]=parenti;
rank[parenti]++;
}
types--;
}
// 统计现在有多少个类别
public int count(){
return types;
}
}
收获:
- 我们可以在Union的时候使用types–来判断类型的变化
- 对于希望用PriorityQueue排序的类E,模版
class E implements PriorityQueue<E>{
@Override
public int compareTo(E other){
return ...;
}
}
思路二:Prim算法 [PriorityQueue或n次循环]
思路:
- 随机选取一个顶点,将所有与之相邻的边都加入到pq之中。
- 选取pq中距离最小的点加入到之前一个点的集合,这个集合就是U。此时我们每次找剩余元素中到U最近的边可以轻松用pq来刻画。如果这个点已经被选过,则就单纯地从pq中去除这个元素。
- 重复2.直到所有顶点都出现了一次
class Solution {
public int minCostConnectPoints(int[][] points) {
int pointNum = points.length;
boolean[] visited = new boolean[pointNum];
int res = 0;
int n = pointNum; //用来记录我们剩余没有选取点的数量
PriorityQueue<Edge> pq = new PriorityQueue<>();
// 选取初始点,这里选points[0]
int crtpoint=0;
while(n>0){
// 为新加的点添加新的边
visited[crtpoint] = true;
n--;
if(n==0)
return res;
for(int j=0;j<pointNum;j++)
if(j!=crtpoint)
pq.add(new Edge(crtpoint,j,distanceManhatton(points[crtpoint],points[j])));
// System.out.println("n = "+n+" pq.length = "+pq.size());
// 选取一个新的点
Edge newEdge;
do{
newEdge = pq.poll();
// System.out.println(newEdge.b+" visited ? "+visited[newEdge.b]);
}while(!pq.isEmpty() && visited[newEdge.b]);
crtpoint = newEdge.b;
res += newEdge.value;
}
return res;
}
public int distanceManhatton(int[] point1,int[] point2){
int res = 0;
for(int i=0;i<point1.length;i++)
res+=Math.abs(point1[i]-point2[i]);
return res;
}
}
class Edge implements Comparable<Edge>{
int a,b; // 一条边的两个顶点编号
int value;
public Edge(int a,int b, int value){
this.a = a;
this.b = b;
this.value = value;
}
@Override
public int compareTo(Edge other) {
return this.value-other.value;
}
}
或者可以不需采用PriorityQueue
class Solution {
public int minCostConnectPoints(int[][] points) {
int vertexes=points.length;
if(vertexes<2) return 0;
int checkPoint=0;
int cost=0;
int[] lowcost=new int[vertexes];
//🌟一开始每个节点的lowcost都很大,每次循环,我们更新该节点到U的距离。(只需要检查新加入的点即可。)
Arrays.fill(lowcost,Integer.MAX_VALUE);
lowcost[0]=0;
//循环n-1次
for(int i=0;i<vertexes-1;i++){
//计算其他结点到生成树的距离
int minDist=Integer.MAX_VALUE;
int temp=checkPoint;
for(int v=0;v<vertexes;v++){
//lowcost[v]==0则表示已经加入到生成树中了
if(lowcost[v]>0){
//计算其他点到生成树的距离
lowcost[v]=Math.min(lowcost[v],manhaton(points,v,checkPoint));
//选择当前最短的距离作为新的检查点
if(lowcost[v]<minDist){
minDist=lowcost[v];
temp=v;
}
}
}
//更新检查点
checkPoint=temp;
//将新的检查点放入最小生成树
lowcost[checkPoint]=0;
//更新总费用
cost+=minDist;
}
return cost;
}
private int manhaton(int[][] points,int x,int y){
return Math.abs(points[x][0]-points[y][0])+Math.abs(points[x][1]-points[y][1]);
}
}