洛谷java实现(P1111修复公路)

题目链接:https://www.luogu.com.cn/problem/P1111

题目类型典型的求最小生成树,可以使用prim算法或者kruskal算法,这里使用kruskal算法!
大致思路:

  1. 对所有边进行排序(权值升序);

  2. 边遍历所有边边用并查集(路径减半优化)去重已经挑选好的边;

  3. 结束条件:挑好的边等于村庄总个数,如果所有边遍历完表明没有最小生成树,则输出-1;

对于排序我们可以选择(手写快速排序)

import java.util.Scanner;

public class Main {
    static class Edge {
        int from;
        int to;
        int weight;
        public Edge(int from, int to, int weight) {
            this.from = from;
            this.to = to;
            this.weight = weight;
        }
    }

    /**
     * 并查集 - 路径减半优化
     */
    static class UnionFind {
        int parents[];

        public UnionFind(int n) {
            parents = new int[n];
            for (int i = 1; i < n; i++) {
                parents[i] = i;
            }
        }
	
        int find(int v) {
            while (v != parents[v]) {
                parents[v] = parents[parents[v]];
                v = parents[v];
            }
            return v;
        }
    }

    static Edge[] edges;//所有边

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        UnionFind uf = new UnionFind(n + 1);
        edges = new Edge[m];
        for (int i = 0; i < m; i++) {
            edges[i] = new Edge(in.nextInt(), in.nextInt(), in.nextInt());
        }
        quickSort(0, edges.length);
        int nums = 0, ans = 0;
        for (Edge edge : edges) {
            int from = uf.find(edge.from);
            int to = uf.find(edge.to);
            if (from == to) continue;//判断是否已选择
            ans = Math.max(ans, edge.weight);
            nums++;
            if (nums == n - 1) {
                System.out.println(ans);
                return;
            }
            //把to和from记为同个集合
            uf.parents[from] = to;
        }
        System.out.println(-1);
    }

    private static void quickSort(int begin, int end) {
        if (end - begin < 2) return;
        int mid = pivotIndex(begin, end);
        quickSort(begin, mid);
        quickSort(mid + 1, end);
    }
	
    private static int pivotIndex(int begin, int end) {
        //随机抽取一个当做轴点,并将其放在begin位置
        swap(begin, begin + (int) ((Math.random() * (end - begin))));
        //备份轴点
        Edge t = edges[begin];
        //将轴点放在合适位置
        end--;//指向最后一个元素
        while (begin < end) {
            while ((begin < end)) {//先从右边开始
                if (t.weight < edges[end].weight) {
                    end--;
                } else {
                    edges[begin++] = edges[end];
                    break;//调换方向
                }
            }
            while ((begin < end)) {
                if (t.weight > edges[begin].weight) {
                    begin++;
                } else {
                    edges[end--] = edges[begin];
                    break;//调换方向
                }
            }
        }
        //将轴点放入
        edges[begin] = t;
        return begin;
    }
    private static void swap(int i, int j) {
        Edge t = edges[i];
        edges[i] = edges[j];
        edges[j] = t;
    }

}

我们也可以使用手写小顶堆来排序


import java.util.Scanner;

public class Main {
    static class Edge {
        int from;
        int to;
        int weight;

        public Edge(int from, int to, int weight) {
            this.from = from;
            this.to = to;
            this.weight = weight;
        }
    }

    /**
     * 并查集 - 路径减半优化
     */
    static class UnionFind {
        int parents[];

        public UnionFind(int n) {
            parents = new int[n];
            for (int i = 1; i < n; i++) {
                parents[i] = i;
            }
        }

        int find(int v) {
            while (v != parents[v]) {
                parents[v] = parents[parents[v]];
                v = parents[v];
            }
            return v;
        }
    }

    /**
     * 小顶堆
     */
    static class minHeap {
        Edge elements[];
        int size;

        public minHeap(Edge[] heap) {
            this.elements = heap;
            size = heap.length;
            hepfiy();
        }

        void hepfiy() {//自下而上的下滤
            for (int i = (size >> 1) - 1; i >= 0; i--) {
                siftDown(i);
            }
        }

        private void siftDown(int index) {
            Edge element = elements[index];
            int half = size >> 1;
            while (index < half) {//拥有子节点的节点
                //两种情况:只有左子节点,左右都有
                int childIndex = (index << 1) + 1;
                Edge child = elements[childIndex];//左子节点
                int rightChild = childIndex + 1;
                if (rightChild < size && child.weight > elements[rightChild].weight) {//右节点比左节点大
                    childIndex = rightChild;
                    child = elements[rightChild];
                }
                if (child.weight >= element.weight) break;
                //父节点小于子节点,交换
                elements[index] = child;
                index = childIndex;
            }
            elements[index] = element;
        }

        Edge poll() {
            Edge first = elements[0];
            elements[0] = elements[--size];
            //下滤
            siftDown(0);
            return first;
        }


        public boolean isEmpty() {
            return size == 0;
        }
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        UnionFind uf = new UnionFind(n + 1);
        Edge[] edges = new Edge[m];
        for (int i = 0; i < m; i++) {
            edges[i] = new Edge(in.nextInt(), in.nextInt(), in.nextInt());
        }
        minHeap queue = new minHeap(edges);
        int nums = 0, ans = 0;
        while (!queue.isEmpty()) {
            Edge edge = queue.poll();
            int from = uf.find(edge.from);
            int to = uf.find(edge.to);
            if (from == to) continue;//判断是否已选择
            ans = Math.max(ans, edge.weight);
            nums++;
            if (nums == n - 1) {
                System.out.println(ans);
                return;
            }
            //把to和from联合
            uf.parents[from] = to;
        }
        System.out.println(-1);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值