Kruskal算法实例

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);
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值