AcWing859. Kruskal算法求最小生成树

一定要看这个链接的讲解视频:强烈推荐!!!【图-最小生成树-Prim(普里姆)算法和Kruskal(克鲁斯卡尔)算法】

1.题目

在这里插入图片描述

2.Kruskal基本思想:

Kruskal算法是一种用于求解加权连通图的最小生成树问题的贪心算法。它的基本思想可以概括为以下几个步骤:

  1. 边排序:将图中的所有边按照权重从小到大进行排序。

  2. 初始化并查集:创建一个并查集来跟踪图中的各个连通分量。开始时,每个顶点都是一个独立的连通分量,即每个顶点的父节点指向自己。

  3. 选择边:从排序后的边列表中选择边,每次选择时,都选择当前最小的边。为了确保添加的边不会形成环,使用并查集来检查当前选择的边的两个顶点是否属于同一个连通分量。

  4. 检查连通分量:在添加边之前,使用并查集的 find 操作来确定边的两个顶点是否已经在同一个连通分量中。如果两个顶点已经在同一个连通分量中,则跳过这条边;如果不在同一个连通分量中,则将它们合并到同一个连通分量中。

  5. 构建最小生成树:重复步骤3和步骤4,直到添加了 ( n-1 ) 条边(其中 ( n ) 是图中顶点的数量),这时就构成了一棵包含所有顶点的最小生成树。

  6. 结束条件:如果在选择边的过程中,发现剩余的边不足以连接所有顶点(即连通分量的数量大于1),则说明图不连通,无法构建最小生成树。

Kruskal算法的效率主要依赖于边排序和并查集的操作。边排序通常使用归并排序或其他高效的排序算法来完成,而并查集提供了快速的连通分量检查和合并操作。算法的时间复杂度主要取决于排序和并查集操作的时间复杂度,通常为 O ( E log ⁡ E ) O(E \log E) O(ElogE),其中 E E E是边的数量。

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010, M = 200010, INF = 0x3f3f3f3f;

int n, m;
int p[N];

struct Edge
{
    int a, b, w;
	//对边权排序的时候会进行Edge的比较,因此需要重载operator<
    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)//不在一个连通分量里面,没有成环,可以在ab点之间添加进去一条边,
        {
            p[a] = b;
            res += w;
            cnt ++ ;
        }
    }
	//n个点,最小生成树里面应该包含n-1条边,如果cnt小于n-1说明有的不连通,只能放弃
    if (cnt < n - 1) return INF;
    return res;
}

int main()
{
    scanf("%d%d", &n, &m);

    for (int i = 0; i < m; i ++ )
    {
        int a, b, w;
        scanf("%d%d%d", &a, &b, &w);
        edges[i] = {a, b, w};
    }

    int t = kruskal();

    if (t == INF) puts("impossible");
    else printf("%d\n", t);

    return 0;
}


3.逐行解释代码:

当然,下面是对代码中每一行的详细解释:

#include <cstring>

包含 C 标准库中的字符串处理函数。

#include <iostream>

包含 C++ 标准库中的输入输出流相关函数。

#include <algorithm>

包含 C++ 标准库中的算法相关函数,比如排序函数 sort

using namespace std;

使用标准命名空间,这样在使用标准库中的函数和对象时就不需要加 std:: 前缀。

const int N = 100010, M = 200010, INF = 0x3f3f3f3f;

定义常量 N 为顶点的最大数量,M 为边的最大数量,INF 为一个很大的数,表示无法连接的状态。

int n, m;

定义变量 n 存储顶点数量,m 存储边的数量。

int p[N];

定义数组 p 用于存储并查集的父节点信息。

struct Edge
{
    int a, b, w;

定义结构体 Edge 用于存储边的信息,包括两个顶点 ab,以及边的权重 w

    bool operator< (const Edge &W)const
    {
        return w < W.w;
    }

重载小于运算符,用于比较两条边的权重,以便后续排序。

}edges[M];

定义数组 edges 存储所有的边。

int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

定义 find 函数,用于并查集的查找操作,如果 x 不是其自己的父节点,则递归查找其父节点,并进行路径压缩。

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

定义 kruskal 函数,并在函数内部对所有边按照权重进行排序。

    for (int i = 1; i <= n; i ++ ) p[i] = i;

初始化并查集,每个顶点的父节点都是自己。

    int res = 0, cnt = 0;

定义变量 res 用于存储最小生成树的总权重,cnt 用于存储已经加入最小生成树的边的数量。

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

如果两个顶点的根节点不同,说明它们不属于同一个集合,可以将它们合并,更新并查集,累加权重到 res,并增加 cnt

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

如果加入最小生成树的边的数量小于顶点数量减一,说明无法构成最小生成树,返回 INF;否则返回最小生成树的总权重。

int main()
{
    scanf("%d%d", &n, &m);

main 函数中,使用 scanf 函数读取顶点和边的数量。

    for (int i = 0; i < m; i ++ )
    {
        int a, b, w;
        scanf("%d%d%d", &a, &b, &w);
        edges[i] = {a, b, w};
    }

读取每条边的信息,包括两个顶点和边的权重,并将它们存储到 edges 数组中。

    int t = kruskal();

调用 kruskal 函数计算最小生成树的权重。

    if (t == INF) puts("impossible");
    else printf("%d\n", t);

根据 kruskal 函数的返回值判断是否能够构成最小生成树,如果不能则输出 “impossible”,否则输出最小生成树的总权重。

    return 0;
}

程序正常结束,返回 0。

weixin073智慧旅游平台开发微信小程序+ssm后端毕业源码案例设计 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

dareu_4523

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

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

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

打赏作者

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

抵扣说明:

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

余额充值