[Java学习] 最小生成树——kruskal算法


前言

上一篇主要介绍了求解最小生成树算法之一的Prim算法。本篇主要介绍的是另一种Kruskal算法。相比于Prim算法,kruskal算法更易理解。


Kruskal算法流程

以下面这个图为例:

在这里插入图片描述

首先将一个图中所有边按照权值大小进行排列,来判断是否所有的边边是否应该加入到集合中。

在这里插入图片描述

接下来是一个回填的过程,在列表中按次序依次取出边回填到图中。每回填一条新边都要判断图中是否存在环,如果存在环则跳过这条边。

这是当前的全部集合,此时总集合边的数量为4。

在这里插入图片描述

此时我们可以看到2-5这条边的值最小,因为2所在的集合与5所在的集合不同,所以可以连接,边数加1。

在这里插入图片描述

而在进行下一条边操作时,当6-8这条边加入时会形成环,因此这条边被舍弃,开始判断下一条边。

在这里插入图片描述

重复上述过程直到已经选择了 n − 1 n-1 n1条边, n n n为顶点的个数,kruskal算法完成。

在这里插入图片描述

Kruskal算法应用实例

题目描述

给定一个 n n n
个点 m m m
条边的无向图,图中可能存在重边和自环,边权可能为负数。

求最小生成树的树边权重之和,如果最小生成树不存在则输出 i m p o s s i b l e impossible impossible

代码解释

解决该问题的代码


import java.io.*;
import java.util.*;

class Edge {
    int a, b, c;
    public Edge(int a, int b, int c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }
}

class Main {
    private static int N = 100010;
    private static int M = 200010;
    private static int[] p = new int[N];
    private static int n, m;
    private static int INF = 0x3f3f3f3f;
    private static Edge[] g = new Edge[M];

    public static int find(int x) {
    //寻找x点的祖宗节点
        if (p[x] != x) {
            p[x] = find(p[x]);
        }
        return p[x];
    }

    public static void main(String[] args) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        String[] str1 = bufferedReader.readLine().split(" ");
        n = Integer.parseInt(str1[0]);
        m = Integer.parseInt(str1[1]);
        for (int i = 1; i <= n; i ++) {
            p[i] = i;//初始化并查集
        }
        for (int i = 0; i < m; i ++) {
            String[] str2 = bufferedReader.readLine().split(" ");
            int a = Integer.parseInt(str2[0]);
            int b = Integer.parseInt(str2[1]);
            int c = Integer.parseInt(str2[2]);

            g[i] = new Edge(a, b, c);
        }
        int t = kruskal();
        if (t == INF) {
            System.out.println("impossible");
        } else {
            System.out.println(t);
        }
        bufferedReader.close();

    }

    public static int kruskal() {
        Arrays.sort(g, 0, m, new Comparator<Edge>(){

           @Override
           public int compare(Edge e1, Edge e2){
               return e1.c - e2.c;
           }
        });
        int res = 0;
        int cnt = 0; //res记录最小生成树的树边权重之和,cnt记录的是全部加入到树的集合中边的数量(可能有多个集合)
        for (int i = 0; i < m; i ++) {
            Edge tmp = g[i];
            int a = tmp.a;
            int b = tmp.b;
            int c = tmp.c;
            a = find(a);
            b = find(b);
            if (a != b) {
            /*
        如果a和b已经在一个集合当中了,说明这两个点已经被一种方式连接起来了,
        如果加入a-b这条边,会导致集合中有环的生成,而树中不允许有环生成,所以一个连通块中的点的数量假设
        为x,那么里面x个节点应该是被串联起来的,有x-1条边,所以只有当a,b所属的集合不同时,才能将a-b这条
        边加入到总集合当中去
        */
                p[a] = b;
                res += c;
                cnt ++;
            }
        }
        if (cnt < n - 1) {
        // 边树小于n-1,不能构造最小生成树
            return INF;
        } else {
            return res;
        }
    }
}


具体的算法流程也可以参照B站上的优质讲解视频:https://www.bilibili.com/video/BV1Eb41177d1?from=search&seid=3240486097514768944&spm_id_from=333.337.0.0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值