0
点赞
收藏
分享

微信扫一扫

最小生成树的两种方法(Kruskal算法和Prim算法)

静守幸福 2022-03-23 阅读 107

图的生成树:一个联通图的生成树是指一个连通子图,它含有图中全部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中最小代价边信息。

举报

相关推荐

0 条评论