2022.3.28 图论——KRUSKAL 最小生成树算法


一、最小生成树

参考文章

1.什么是最小生成树?

「树」和「图」的根本区别:树不会包含环,图可以包含环。

「生成树」就是在图中找一棵包含图中的所有节点的树。

如果一幅图没有环,完全可以拉伸成一棵树的模样。说的专业一点,树就是「无环连通图」。即生成树是含有图中所有顶点的「无环连通子图」。

比如下面这幅图,红色的边就组成了两棵不同的生成树:
在这里插入图片描述

一般来说,我们都是在无向加权图中计算最小生成树的,那么最小生成树就是所有可能的生成树中,权重和最小的那棵生成树。

2.如何判断图是否能生成树?

在这里我们使用前文 Union-Find 并查集算法运用 中介绍过的 Union-Find 算法,它在 Kruskal 算法中的主要作用是保证最小生成树的合法性(保证生成的是棵树,即不包含环)。

如以下条件:
给你输入编号从0到n - 1的n个结点,和一个无向边列表edges(每条边用节点二元组表示),请你判断输入的这些边组成的结构是否是一棵树。

对于这道题,我们可以思考一下,什么情况下加入一条边会使得树变成图(出现环)?
对于添加的这条边,如果该边的两个节点本来就在同一连通分量里,那么添加这条边会产生环;反之,如果该边的两个节点不在同一连通分量里,则添加这条边不会产生环。

输入:n = 5
edges = [[0,1],[1,2],[2,3],[1,3],[1,4]],但生成了不同的树。
无环:
在这里插入图片描述
有环:
在这里插入图片描述

// 判断输入的若干条边是否能构造出一棵树结构
boolean validTree(int n, int[][] edges) {
   
    // 初始化 0...n-1 共 n 个节点
    UF uf = new UF(n);
    // 遍历所有边,将组成边的两个节点进行连接
    for (int[] edge : edges) {
   
        int u = edge[0];
        int v = edge[1];
        // 若两个节点已经在同一连通分量中,会产生环
        if (uf.connected(u, v)) {
   
            return false;
        }
        // 这条边不会产生环,可以是树的一部分
        uf.union(u, v);
    }
    // 要保证最后只形成了一棵树,即只有一个连通分量
    return uf.count() == 1;
}

class UF {
    
    // 见上文代码实现
}

3.Kruskal 算法

树的判定算法加上按权重排序的逻辑就变成了 Kruskal 算法

所谓最小生成树,就是图中若干边的集合,你要保证这些边:

1、包含图中的所有节点。
2、形成的结构是树结构(即不存在环)。
3、权重和最小。

上文说到前两条可以地利用 Union-Find 算法做到,关键在于第 3 点,用贪心思路来保证得到的这棵生成树是权重和最小的:
将所有边按照权重从小到大排序,从权重最小的边开始遍历,如果这条边和mst中的其它边不会形成环,则这条边是最小生成树的一部分,将它加入mst集合;否则,这条边不是最小生成树的一部分,不要把它加入mst集合。最后mst集合中的边就形成了最小生成树
(mst是图中若干边的集合,Minimum Spanning Tree最小生成树的缩写)

二、例题

第一题:1135.最低成本联通所有城市

在这里插入图片描述


答案代码:

int minimumCost(int n, int[][] connections) {
   
    // 城市编号为 1...n,所以初始化大小为 n + 1
    UF uf = new UF(n + 1);
    // 对所有边按照权重从小到大排序
    Arrays.sort(connections, (a, b) -> (a[2] - b[2]));
    // 记录最小生成树的权重之和
    int mst = 0;
    for (int[] edge : connections) {
   
        int u = edge[0];
        int v = edge[1];
        int weight = edge[2];
        // 若这条边会产生环,则不能加入 mst
        if (uf.connected(u, v)) {
   
            continue;
        }
        // 若这条边不会产生环,则属于最小生成树
        mst += weight;
        uf.union(u, v);
    }
    // 保证所有节点都被连通
    // 按理说 uf.count() == 1 说明所有节点被连通
    // 但因为节点 0 没有被使用,所以 0 会额外占用一个连通分量
    return uf.count(
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值