1. 程式人生 > 實用技巧 >最小生成樹演算法(Prim, Kruskal)

最小生成樹演算法(Prim, Kruskal)

連線所有點的最小費用

class Solution {
    public int minCostConnectPoints(int[][] points) {

        //prim演算法,時間複雜度 n^2

        int res = 0, n = points.length;

        int[][] g = new int[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                g[i][j] = Math.abs(points[i][0] - points[j][0]) + Math.abs(points[i][1] - points[j][1]);
            }
        }

        
//點的集合,已經放入集合為true boolean[] v = new boolean[n]; //lowest[i] 表示 點i 到已放入集合的點的最短距離 int[] lowest = new int[n]; //先放入 點0 v[0] = true; //未放入點 i 到已放入點的集合的最短距離,這裡是[0],[1, n-1] for (int i = 1; i < n; i++) { lowest[i] = g[0][i]; } //還有n - 1個點沒有放入集合
for (int i = 1; i < n; i++) { int min = Integer.MAX_VALUE, index = -1; for (int j = 0; j < n; j++) { if (v[j]) continue; if (min > lowest[j]) { index = j; min = lowest[j]; } } res
+= min; v[index] = true; //更新lowest for (int j = 0; j < n; j++) { if (v[j]) continue; lowest[j] = Math.min(lowest[j], g[index][j]); } } return res; } }

class Solution {
    public int minCostConnectPoints(int[][] points) {

        //Kruskal演算法
        int n = points.length, res = 0;

        //優先佇列(小到大)
        PriorityQueue<Edge> heap = new PriorityQueue<>((o1, o2) -> o1.dis - o2.dis);
        //並查集找環
        UF uf = new UF(n);

        //按邊的大小從小到達放入優先佇列
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                int dis = Math.abs(points[i][0] - points[j][0]) + Math.abs(points[i][1] - points[j][1]);
                heap.offer(new Edge(i, j, dis));
            }
        }

        int mark = 0;
        while (!heap.isEmpty()) {
            Edge e = heap.poll();
            if (uf.union(e.x, e.y)){
                res += e.dis;
                mark++;
            }
            if (mark == n - 1) break;
        }

        return res;
    }


    class Edge {
        private int x, y, dis;
        public Edge(int x, int y, int dis) {
            this.x = x;
            this.y = y;
            this.dis = dis;
        }
    }

    //用並查集
    class UF{
        int[] parent;

        public UF(int n) {
            parent = new int[n];
            for (int i = 0; i < n; i++) {
                parent[i] = i;
            }
        } 

        private int find(int x) {
            if (x != parent[x]) {
                parent[x] = find(parent[x]);
            }
            return parent[x];
        }

        public boolean union(int a, int b) {
            int rootA = find(a);
            int rootB = find(b);
            //如果a, b在連通,那麼加入a, b就會有環
            if (rootA == rootB) return false;
            parent[rootA] = rootB;
            return true;
        }
    }
}