Kruskal算法实例

巧了我就是萌 提交于 2019-12-10 15:45:17
package com.PTA.graph.Kruskal;

import java.util.*;

/**
 * 公路村村通问题
 * 算法: 最小生成树  Kruskal
 * 无向有权图
 * 图存储--- 邻接矩阵
 */
public class Kruskal {

    static Scanner cin = new Scanner(System.in);
    /** 点数, 边数, 从1开始*/
    static int Nv, Ne;
    static ArrayList<Edge> edges;
    /** 并查集, 点数*/
    static int[] s;

    static class Edge implements Comparable<Edge>{
        int v1, v2;
        int w; // 权重
        Edge(int v1, int v2, int w){
            this.v1 = v1;
            this.v2 = v2;
            this.w = w;
        }
        @Override
        public int compareTo(Edge o) {
            return w - o.w;
        }
    }

    /** 找到一个结点所在的集合*/
    static int find(int x) {
        if (s[x] < 0) { /* 本身为一个集合*/
            return x;
        }else {
            return s[x] = find(s[x]);
        }
    }

    /** 合并集合*/
    static int union(int root1, int root2) {
        if(s[root1] > s[root2]) { /* 如果集合2比较大 */
            s[root2] += s[root1];
            s[root1] = root2;    /* 集合1并入集合2  */
            return  root2;
        }else{
            s[root1] += s[root2];
            s[root2] = root1;    /* 集合2并入集合1  */
            return  root1;
        }

    }

    /** 监测俩个是否为一个集合*/
    static boolean isSameSet(int x, int y) {
        return find(x) == find(y);
    }

    static void kruskal(){
        /** 生成树边计数*/
        int edgeN = 0;
        /** 最小生成树权重和*/
        int cost = 0;
        /** 下一个权重最小边的位置 */
        int nextEdge = 0;
        s = new int[Nv+1];
        /** 初始化并查集*/
        Arrays.fill(s,-1);
        Collections.sort(edges); /* 对边集 合排序, 或者使用最小堆*/
        while (edgeN < Nv-1) { /* 边未收集完成*/
            if (nextEdge >= Ne) {
                break;
            }
            int v1 = edges.get(nextEdge).v1;
            int v2 = edges.get(nextEdge).v2;
            if (!isSameSet(v1, v2)){ /*两个不是一个集合(没有形成环路)*/
                cost += edges.get(nextEdge).w;
                edgeN++;
                union(find(v1), find(v2));
            }
            nextEdge++;
        }
        if (edgeN < Nv-1){
            cost = -1; /* 图不连通*/
        }
        System.out.println(cost);
    }

    public static void main(String[] args) {
        Nv = cin.nextInt();
        Ne = cin.nextInt();
        edges = new ArrayList<>();
        if (Ne < Nv-1) { /* 边数不够, 图不连通*/
            System.out.println("-1");
        }else {
            for (int i = 0; i < Ne; i++) {
                int from = cin.nextInt();
                int to = cin.nextInt();
                int w = cin.nextInt();
                edges.add(new Edge(from, to, w));
            }
            kruskal();
        }
    }
}

  • 通过最小堆(优先队列)优化算法
package com.PTA.graph.Kruskal;

import java.util.*;

/**
 * 算法: 最小生成树  Kruskal
 * 优先队列(最小堆)
 */
public class Main {

    static Scanner cin = new Scanner(System.in);
    static int Nv, Ne;
    static Queue<Edge> edges; // 边集合
    static int[] s;

    static class Edge implements Comparable<Edge> {
        int v1, v2;
        int w; // 权重
        Edge(int v1, int v2, int w){
            this.v1 = v1;
            this.v2 = v2;
            this.w = w;
        }
        @Override
        public int compareTo(Edge o) {
            return w - o.w;
        }
    }

    public static void main(String[] args) {
        Nv = cin.nextInt();
        Ne = cin.nextInt();
        edges = new PriorityQueue<>();
        if(Ne < Nv - 1) { // 边数不够
            System.out.println("-1");
        }else {
            for (int i = 0; i < Ne; i++) { // 收集每一条边
                int from = cin.nextInt();
                int to =cin.nextInt();
                int w = cin.nextInt();
                edges.add(new Edge(from, to, w));
            }
            kruskal();
        }

    }

    static void kruskal() {
        int edgCount = 0; // 生成树计数
        int cost = 0; // 生成树权重和
        // 初始化并查集
        s = new int[Nv+1];
        Arrays.fill(s, -1);
        // 队列头
        int index = 0;

        while (edgCount < Nv - 1) {
            if(index >= Ne) {  // 边收完
                break;
            }
            int v1 = edges.peek().v1;
            int v2 = edges.peek().v2;
            if(isSameUnion(v1, v2) == false) {
                edgCount++; // 收录一条边
                cost += edges.peek().w;
                union(find(v1), find(v2));
            }
            index++;
            edges.poll();
        }
        if (edgCount < Nv-1) { // 图不连通
            cost = -1;
        }
        System.out.println(cost);
    }

    // 将 root1 和root2 合并为一个集合 |负数|表示 集合的元素个数
    static void union(int root1, int root2) {
        if (s[root1] < s[root2]) { // 集合 1 大
            s[root1] += s[root2];
            s[root2] = root1;
        }else {
            s[root2] += s[root1];
            s[root1] = root2;
        }
    }

    // 找元素 x 的根节点
    static int find(int x) {
        if (s[x] < 0) {
            return x;
        }else {
            return s[x] = find(s[x]); // 同时压缩路径
        }
    }

    // 两个元素是否为一个并查集
    static boolean isSameUnion(int x1, int x2) {
        return find(x1) == find(x2);
    }

}

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!