2021-01-19 1584. 连接所有点的最小费用[Kruskal算法,Prim算法]

1584. 连接所有点的最小费用

思路一:Kruskal算法 [PriorityQueque + UnionFind]

思路:

  1. 将所有的边一次性全部读入并存储到PriorityQueue中,权重小的在priority queue上方。
  2. 将pq中的元素一一poll出来,这里使用UnionFind的方式,如果发现有环,就不添加这条边。
  3. 重复做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;
    }
}

收获:

  1. 我们可以在Union的时候使用types–来判断类型的变化
  2. 对于希望用PriorityQueue排序的类E,模版
class E implements PriorityQueue<E>{
	
	@Override
	public int compareTo(E other){
		return ...;
	}
}

思路二:Prim算法 [PriorityQueue或n次循环]

思路:

  1. 随机选取一个顶点,将所有与之相邻的边都加入到pq之中。
  2. 选取pq中距离最小的点加入到之前一个点的集合,这个集合就是U。此时我们每次找剩余元素中到U最近的边可以轻松用pq来刻画。如果这个点已经被选过,则就单纯地从pq中去除这个元素。
  3. 重复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]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值