Acwing-基础算法课笔记之搜索与图论(Kruskal算法)

Acwing-基础算法课笔记之搜索与图论(Kruskal算法)

一、Kruskal算法

1、概述

Kruskal算法的原理简单直接,有以下两个基本操作:
(1)对边的长度贪心并加如T中。先对边长排序,一般直接用sort()函数排序,然后依次把最短的边加入T中。
(2)判断圈。每次加入新的边,判断它是否和已经加入T的边形成了圈,也就是判断连通性。用BFS或DFS也能判断,但是最高效的方法是并查集,并查集是Kruskal算法的绝配。

Kruskal算法复杂度分析:
1、主要操作是排序,复杂度为 O ( m log ⁡ 2 m ) O(m\log_{2}m) O(mlog2m)
2、用并查集能以 O ( m ) O(m) O(m)的复杂度完成所有新加入边的判圈操作。
以上两者相加,总复杂度仍然为 O ( m log ⁡ 2 m ) O(m\log_{2}m) O(mlog2m)

2、过程模拟

下图是Kruskal算法的执行过程,重点是用并查集判圈:
在这里插入图片描述
(1)初始时最小生成树T为空。令S是以点 i 为元素的并查集,开始时,每个点属于独立的集。下面区分了节点 i 和并查集S:

S: 1 ‾ \underline{1} 1 2 ‾ \underline{2} 2 3 ‾ \underline{3} 3 4 ‾ \underline{4} 4 5 ‾ \underline{5} 5
i i i 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5

(2)加入第一条最短边(1-2),T={1-2}。并查集S中,把点2合并到节点1,也就是把点2的集 1 ‾ \underline{1} 1

S: 1 ‾ \underline{1} 1 1 ‾ \underline{1} 1 3 ‾ \underline{3} 3 4 ‾ \underline{4} 4 5 ‾ \underline{5} 5
i i i 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5

(3)加入第2条边(3-4),T={1-2,3-4}。并查集S中,点4合并到点3。

S: 1 ‾ \underline{1} 1 1 ‾ \underline{1} 1 3 ‾ \underline{3} 3 3 ‾ \underline{3} 3 5 ‾ \underline{5} 5
i i i 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5

(4)加入第3条最短边(2-5),T={1-2,3-4,2-5}。并查集S中,把点5合并到点2,也就是把点5的集 5 ‾ \underline{5} 5改成点2的集 1 ‾ \underline{1} 1。在集 1 ‾ \underline{1} 1中,所有点都指向了根,这样做能避免并查集的长链问题,这是“路径压缩”。

S: 1 ‾ \underline{1} 1 1 ‾ \underline{1} 1 3 ‾ \underline{3} 3 3 ‾ \underline{3} 3 1 ‾ \underline{1} 1
i i i 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5

(5)加入第4条最短边(1-5),检查并查集S,发现点5已经属于集 1 ‾ \underline{1} 1,丢弃这条边。这一步实际上是发现了一个圈。并查集的作用就体现在这里。
(6)加入第5条最短边(2-4)。并查集S中,把点4的集并到点2的集。注意这里点4原来属于 3 ‾ \underline{3} 3,实际上修改的是把节点3的集 3 ‾ \underline{3} 3改成集 1 ‾ \underline{1} 1

S: 1 ‾ \underline{1} 1 1 ‾ \underline{1} 1 1 ‾ \underline{1} 1 3 ‾ \underline{3} 3 1 ‾ \underline{1} 1
i i i 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5

(7)对所有边执行上述操作,直到结束。读者可以练习加最后两条边(3-5)、(4-5),这2条边都会形成圈。

3、Kruskal算法模板

Kruskal算法的编码很简单,不管是存图的数据结构还是算法,都很容易编码。
(1)存图。不需要用邻接矩阵、邻接表、链式前向星等,只用最简单、最省空间的结构体数组存边。
(2)编码。基本上就是并查集操作。

时间复杂度是 O ( m l o g m ) O(mlogm) O(mlogm) n n n表示点数, m m m表示边数

int n, m;       // n是点数,m是边数
int p[N];       // 并查集的父节点数组

struct Edge     // 存储边
{
    int a, b, w;

    bool operator< (const Edge &W)const
    {
        return w < W.w;
    }
}edges[M];

int find(int x)     // 并查集核心操作
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int kruskal()
{
    sort(edges, edges + m);

    for (int i = 1; i <= n; i ++ ) p[i] = i;    // 初始化并查集

    int res = 0, cnt = 0;
    for (int i = 0; i < m; i ++ )
    {
        int a = edges[i].a, b = edges[i].b, w = edges[i].w;

        a = find(a), b = find(b);
        if (a != b)     // 如果两个连通块不连通,则将这两个连通块合并
        {
            p[a] = b;
            res += w;
            cnt ++ ;
        }
    }

    if (cnt < n - 1) return INF;
    return res;
}

Acwing-Kruskal算法模板

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会敲代码的狗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值