LeetCode. 1584. 连接所有点的最小费用(最小生成树/Kruskal 算法/并查集)


题目

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

方法一:Kruskal 算法

Kruskal 算法是一种常见并且好写的最小生成树算法。该算法的基本思想是从小到大加入边,是一个贪心算法。

其算法流程为:

  1. 假设连通图 G = ( V , E ) G=(V,E) G=(VE),令最小生成树的初始状态为只有 n n n个顶点而无边的非连通图 T = ( V , { } ) T=(V,\{\}) T=(V{}),图中每个顶点自成一个连通分量
  2. E E E中选择代价最小的边,若该边依附的顶点分别在 T T T中不同的连通分量上,则将此边加入到 T T T中;否则,舍去此边而选择下一条代价最小的边
  3. 依此类推,直至 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) 的空间。

方法二:建图优化的 Kruskal

Reference

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xylitolz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值