leetcode
leetcode 951 ~ 1000
最低成本连通所有城市

最低成本连通所有城市

难度:

标签:

题目描述

代码结果

运行时间: 68 ms, 内存: 20.5 MB


/*
 * 思路:
 * 1. 使用Kruskal算法来找到最小生成树(Minimum Spanning Tree, MST),从而找到连通所有城市的最低成本。
 * 2. 首先将所有的边按照权重进行排序。
 * 3. 使用并查集(Union-Find)来检测是否会形成环,并构建最小生成树。
 * 4. 使用Java Stream API来处理集合和数组操作。
 */
import java.util.*;
import java.util.stream.*;

public class Solution {
    class Edge implements Comparable<Edge> {
        int city1, city2, cost;
        public Edge(int city1, int city2, int cost) {
            this.city1 = city1;
            this.city2 = city2;
            this.cost = cost;
        }
        public int compareTo(Edge other) {
            return this.cost - other.cost;
        }
    }

    public int minimumCost(int n, int[][] connections) {
        List<Edge> edges = Arrays.stream(connections)
            .map(conn -> new Edge(conn[0] - 1, conn[1] - 1, conn[2]))
            .sorted()
            .collect(Collectors.toList());
        UnionFind uf = new UnionFind(n);
        int totalCost = edges.stream()
            .filter(edge -> uf.union(edge.city1, edge.city2))
            .mapToInt(edge -> edge.cost)
            .sum();
        return uf.getCount() == n - 1 ? totalCost : -1;
    }

    class UnionFind {
        private int[] parent, rank;
        private int count;
        public UnionFind(int size) {
            parent = new int[size];
            rank = new int[size];
            count = 0;
            for (int i = 0; i < size; i++) {
                parent[i] = i;
                rank[i] = 1;
            }
        }
        public int find(int p) {
            if (parent[p] != p) {
                parent[p] = find(parent[p]);
            }
            return parent[p];
        }
        public boolean union(int p, int q) {
            int rootP = find(p);
            int rootQ = find(q);
            if (rootP == rootQ) return false;
            if (rank[rootP] > rank[rootQ]) {
                parent[rootQ] = rootP;
            } else if (rank[rootP] < rank[rootQ]) {
                parent[rootP] = rootQ;
            } else {
                parent[rootQ] = rootP;
                rank[rootP]++;
            }
            count++;
            return true;
        }
        public int getCount() {
            return count;
        }
    }
}

解释

方法:

此题解采用了最小生成树(Kruskal算法)的策略来连接所有城市,要求最小的成本。首先,判断边的数量是否少于n-1,若是,则直接返回-1,因为不可能连接所有城市。使用并查集来维护和查询城市间的连通性。城市和其连接边按照连接成本从低到高排序。遍历排序后的边,使用并查集的find函数来查找每条边两个城市的根节点,如果根节点不同,则使用unite函数合并这两个根节点,并累加当前边的成本到总成本中。当选取的边数达到n-1时,所有城市已经连通,返回累计的最小成本。如果循环结束后,选取的边数仍然少于n-1,则返回-1表示不能连通所有城市。

时间复杂度:

O(m log m)

空间复杂度:

O(n + m)

代码细节讲解

🦆
在并查集中,路径压缩的主要目的是什么,它如何帮助提高查找根节点的效率?
路径压缩的主要目的是减少树的高度,从而加速未来对同一组元素的查找操作。在执行查找操作时,路径压缩将沿途的所有节点直接连接到根节点上。这样,下次从这些节点出发的查找操作将直接到达根节点,显著减少查询路径的长度,从而提高效率。
🦆
为什么在选择边时,必须先对边按照成本从低到高进行排序?这种排序对算法的最终结果有什么影响?
按成本从低到高排序是为了确保每次选取的边是当前可用边中成本最低的,这是Kruskal算法的核心。这种策略保证了算法能找到最小生成树,即连接所有节点的最小总成本。如果不按此方法排序,则可能选取成本较高的边,导致无法达到最小成本的目的。
🦆
如果在处理过程中,两个城市的根节点相同,为什么不需要合并这两个节点?这种情况会对算法的运行结果产生什么影响?
如果两个城市的根节点相同,意味着它们已经在同一个连通分量中,即已经连通。此时再合并它们是多余的,不会对连通性产生影响。避免这种不必要的合并可以减少算法的冗余操作,保持已有的连通分量不被破坏,同时防止添加多余的成本。
🦆
你提到了如果边的数量少于n-1则无法连通所有城市,这个结论是怎么得出的?是否有数学或逻辑上的支持?
这个结论基于图论中的基本概念。在一个含有n个节点的树中,必须有至少n-1条边才能使所有节点连通而无环。因此,如果一个图中的边数少于n-1,那么至少有一个节点无法通过边与其他节点连通,无法形成一个覆盖所有节点的连通图。因此,边数少于n-1意味着无法连通所有城市。

相关问题