《最小生成树算法详解:Kruskal的优雅实现》

前置知识和本篇介绍

Kruskal算法需要借助以下两种数据结构。请读者朋友务必掌握后再阅读此篇。
前置知识: 数据结构:优先级队列[可以用排序替代]- 数据结构:并查集
编程语言:Java/C++.

不是所有图算法都要依赖建图,Kruskal算法是典型的不建图就可以用的图算法之一。

本篇介绍—最小生成树的概念以及Kruskal算法(读作克鲁斯卡尔算法, 一般可以用K算法代称)。 另外最小生成树算法还有一个Prim算法(普林算法), 这一篇我发到另一篇文章[探究经典Prim算法写法](https://blog.youkuaiyun.com/2303_79972050/article/details/144041234?spm=1001.2014.3001.5501)

《最小生成树算法详解:Kruskal的优雅实现》


一. 引言

  • 什么是最小生成树(MST)?
  • 简要介绍 Kruskal 算法的核心思想和优势。

一家通信公司试图建立其各个计算机中心的通信联系。
它可以通过租用电话线连接任意之间的一对, 但不过这样成本太高了。
能保证计算机中心能两两之间有通路即可吗?
比如这样以下的一个无向图, 如何保证其连通呢?
离散数学告诉我们, 简单连通无向图保证连通性是一棵树, 树状结构就可以保证连通性, 这棵树叫做生成树。
而这个图还带有权值, 由此衍生处一个最小生成树的概念。即生成树各边的权值之和最小的生成树。

以下是来自wiki的图,顶点表示计算机中心, 顶点之间用边表示, 其中边带有属性的权值就是费用。
从一个图中抽掉那些高昂的费用但不破坏图的连通性。
由于我们只需要保证连通和最低费用就可以了。 任意两个顶点之间只需要一条边连接就足够了。(生成树)
那么,按照上述删除连通无向图中的边的最终结构会形成一棵树(如下图黑色的边形成的树)。
这棵树就是最小生成树。
在这里插入图片描述

最小生成树的两种算法都是基于贪心的策略, 即寻找局部最优解。
Kruskal算法是一种贪心算法, 它从边的角度出发, 用于带权无向图在寻找最小生成树。
下面讲述最小生成树的性质和算法流程。

2. 最小生成树的性质

  • 什么图具有最小生成树:
      1. 满足无向,带权,连通这些特征的图具有最小生成树。
      1. 生成树对于无向连通图的概念, 最小生成树要针对带权图, 否则就无"最小"的概念。
      1. 生成树存在是最小生成树存在的必要条件, 对于无向带权图来说,只要存在一棵生成树, 那么最小生成树就是它本身; 如果存在多棵生成树, 那么最小生成树就是生成树权值比较最小的结果。
  • 最小生成树的定义:
    • 连通图中所有的顶点并且边权重之和最小的树结构。
  • 相关性质:
    • 最小生成树本质是树结构, 它满足树的性质
    • 生成树的边数为 n − 1 n-1 n1,n是无向连通图的顶点个数。
    • 不会成环。
    • 如何判断最小生成树是否存在? 道理很简单,dfs或者bfs判断连通性即可, 或者可以统计边数是否满足n-1(下面的代码也是通过这种方式证明最小生成树不存在的)。
  • 最小生成树不唯一:
    最小生成树的权值要求最小,但边的组织可能不同, 因此结构并不唯一。 存在多种最小生成树结构(最小权重和是相同的)。

3. Kruskal 算法详解

  • 3.1 Kruskal 的核心思想
    • 每次选择最小的边, 将边添加进最小生成树结构里。期间可能存在成环情况(这里借助并查集判环, 具体细节看代码部分), 直到添加n-1条边结束。
  • 3.2 算法流程
      1. 若题目中给了图或者可以选择一种建图方式自己建好图, 遍历图中所有的边将它们排好序; 或者,收集好边的信息(两个端点, 权重)
      1. 按边权值升序排序。
      1. 使用并查集判断边是否形成环。(具体逻辑是判断两个端点是否在同一集合, 若存在则端点已经添加进最小生成树了必须舍弃该边; 否则加入最小生成树,并两个端点合并到一个集合)
      1. 逐步加入边,直到添加到n-1条边为止。
  • 3.3 为什么正确性
    基于贪心的策略, 每次选择权值最小的边且这条边不会成环或破坏连通性。 局部最优 == 全局最优。严格证明不必纠结。
    如果读者纠结证明可以阅读相关帖子, 算法导论, 或者直接读原作者的论文。

4. Kruskal 算法的实现

  • 4.1 数据结构选择
    • 使用并查集(Union-Find)O(1)的时间进行合并, 高效地检测环。
    • 前面可以通过排序数组, 可以选择优先级队列或者更多广泛的堆结构(可合并堆)。
  • 4.2 实现细节
    • 对边信息的处理
    • 并查集实现:路径压缩,按秩优化(我这里代码并未实现)
  • 4.3 Java代码实现
    Kruskal模板题
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Arrays;
public class Main{
   
    //最大数据量
    public static int MAXN = 5001;
    public static int MAXM = 200001;
    
    //并查集模板数组
    public static int[] father = new int
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值