【第二十三课】最小生成树:prime 和 kruskal 算法(acwing858,859 / c++代码 )

这篇具有很好参考价值的文章主要介绍了【第二十三课】最小生成树:prime 和 kruskal 算法(acwing858,859 / c++代码 )。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

前言

Prime算法--加点法

acwing-858 

代码如下

一些解释 

Kruskal算法--加边法

acwing-859

并查集与克鲁斯卡尔求最小生成树 

代码如下

一些解释  


前言

之前学最短路的时候,我们都是以有向图为基础的,当时我们提到如果是无向图,只要记得两个顶点处都要加边就好了。

而在最小生成树的问题中,我们所面临的大多都是无向图。

这个姐姐👇对这两种算法的讲解非常清晰,没有代码部分,但是对于理解这两种算法的做法很有帮助,推荐看一下。 

【数据结构 图 最小生成树 Prime和Kruskal算法】

【第二十三课】最小生成树:prime 和 kruskal 算法(acwing858,859 / c++代码 ),算法--学习笔记,算法,图论,c++

截取自视频。

感觉总结的很好,就搬过来啦(侵删) 

Prime算法--加点法

prime算法也叫加点法,主要是通过不断将所有顶点都加入到生成树中实现的。

利用该算法求最小生成树的步骤就是:

从任意1个顶点开始,在其他所有顶点中,选出一个离它距离最近的顶点,将其与该顶点进行连线;之后我们看其他的顶点中   离这两个已经选中的点  之间的距离最短的点,再将其连线......

由此我们可以总结出,我们要看的是:其他顶点中 到已经选出的这些顶点的集合 距离最短的点,我们把这个集合称为生成树,这里可以理解哈。

因此我们可以判断dist数组的含义应该是:存储每一个顶点到 集合(也就是生成树) 的最短距离。

prime算法的代码和dijkstra算法的实现是差不多的,主要区别就是dist数组的含义。前者是找离这个集合最短距离的点,后者找的是离某个源点距离最短的点

下面这个图模拟我们prime算法的手算的步骤

【第二十三课】最小生成树:prime 和 kruskal 算法(acwing858,859 / c++代码 ),算法--学习笔记,算法,图论,c++

方便大家理解啦~ 

prime算法时间复杂度是O(n^2),适用于解决稠密图的问题。 

下面是模板题:

acwing-858 

【第二十三课】最小生成树:prime 和 kruskal 算法(acwing858,859 / c++代码 ),算法--学习笔记,算法,图论,c++

可以看出数据范围边数远大于点数,属于稠密图。

与dijkstra算法的思路是差不多的,直接看代码把 

代码如下

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=510, INF=0x3f3f3f3f;
int n,m;
int g[N][N];
int dist[N];//存储每一个顶点到 集合(也就是生成树) 的最短距离
bool st[N];
int prime()
{
    memset(dist,0x3f,sizeof dist);
    int ans=0;
    for(int i=0;i<n;i++)//要加入所有的顶点,因此要循环n次
    {
        int t=-1;
        for(int j=1;j<=n;j++)
        {
            if(!st[j] && (t==-1 || dist[t]>dist[j]))
            {
                t=j;
            }
        }
        if(i && dist[t]==INF)return INF;
        if(i)ans+=dist[t];//第一个顶点权值是0,没必要再加一次,因此存在该if语句
        //选中t之后,比较原来的各个顶点到生成树的距离 与 各顶点与t顶点的权值的大小关系
        for(int j=1;j<=n;j++)
        {
            dist[j]=min(dist[j],g[t][j]);
        }
        st[t]=1;
    }
    return ans;
}
int main()
{
    cin>>n>>m;
    memset(g,0x3f,sizeof g);
    for(int i=0;i<m;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        g[a][b]=g[b][a]=min(g[a][b],c);
    }
    int t=prime();
    if(t==INF)puts("impossible");
    else cout<<t<<endl;
    return 0;
}

一些解释 

1.if(i && dist[t]==INF)return INF; 

这里我们判断除了第一个顶点之外的其他顶点,到生成树的距离是否是无穷大,如果是无穷大说明图不连通,无法构成生成树

由于我们外层循环只控制循环次数,表示要加入n个顶点,且i从0开始,说明了第一个顶点是作为第0次循环实现的,因此这里排除第一个顶点,直接判断 i 就可以

为什么要跳过第一个顶点?

如果我们不跳过第一个顶点,那么在第一次循环时,由于所有顶点到生成树的距离都被初始化为无穷大,所以会直接返回无穷大,这显然是不正确的。因此,我们需要在第一次循环时跳过这个检查。

2.dist[j]=min(dist[j],g[t][j]); 

这里遍历各个顶点,判断 其原始的dist[j]与添加了 t 顶点之后,t与j顶点之间的权值 的大小关系,从而更新出每个顶点到生成树的距离。(因为既然t已经被加入到生成树中,那么到t的权值也就是到生成树的距离啦。)

把prime与dijkstra的代码放在一起对比一下

【第二十三课】最小生成树:prime 和 kruskal 算法(acwing858,859 / c++代码 ),算法--学习笔记,算法,图论,c++

Kruskal算法--加边法

kruskal算法与prime对应是加边法,主要通过不断加边,连接到所有顶点之后就得到了最小生成树。

利用这种方法求最小生成树的步骤是:

在所有的边中不断的找最小的边加入到我们最小生成树的集合中,直到将所有顶点都连入。在加边过程中,避免成环即可。

曾经学数据结构的时候,手算我还是比较喜欢用克鲁斯卡尔算法的哈哈哈,感觉加边理解上好像更简单一点。

acwing-859

【第二十三课】最小生成树:prime 和 kruskal 算法(acwing858,859 / c++代码 ),算法--学习笔记,算法,图论,c++

并查集与克鲁斯卡尔求最小生成树 

我们记得在并查集算法中,进行两个集合的合并和查找操作,就是利用树型结构实现的,在克鲁斯卡尔算法求最小生成树时,我们最终就是将顶点都连在一起算是得到了最小生成树,因此我们可以想着利用并查集的思想来实现克鲁斯卡尔求最小生成树。

嗯,,可以想一下二者的联系。我通过这样可以理解二者的关联。

下面是gpt的解释,更全面和专业一点hh,可以看看帮助理解一下~

【第二十三课】最小生成树:prime 和 kruskal 算法(acwing858,859 / c++代码 ),算法--学习笔记,算法,图论,c++

应该是可以理解啦。 

需要的话可以回顾一下并查集的知识,之前写过哒

【第十四课】并查集(acwing-837连通块中点的数量 / c++代码 / 思路详解) 

代码如下

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2e5+10;
int n,m;
int p[N];
struct Edge{
    int a,b,w;
    //运算符重载函数
    bool operator< (const Edge &W)const{
        return w<W.w;
    }
}edges[N];
int find(int x)
{
    if(p[x]!=x)p[x]=find(p[x]);
    return p[x];
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<m;i++)
    {
        int a,b,w;
        cin>>a>>b>>w;
        edges[i]={a,b,w};
    }
    
    sort(edges,edges+m);
    //每个顶点都单独处在一个集合里
    for(int i=1;i<=n;i++)p[i]=i;

    int res=0,count=0;//res累加权值 count存储加入的边数

    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;
            count++;
        }
    }
    if(count<n-1)puts("impossible");//如果边数并不符合 说明不存在最小生成树
    else cout<<res;
    return 0;
}

一些解释  

sort(edges,edges+m);

这里我们调用sort函数,直接写的edge结构体-edge+m,就是因为在结构体中我们定义了重载

//运算符重载函数
    bool operator< (const Edge &W)const{
        return w<W.w;
    }

因为结构体中含有多个变量,如果不定义运算符重载,那么在使用 sort 函数等需要比较边的权值大小的地方,编译器将无法确定如何比较两个 Edge 对象 。

【第二十三课】最小生成树:prime 和 kruskal 算法(acwing858,859 / c++代码 ),算法--学习笔记,算法,图论,c++

【第二十三课】最小生成树:prime 和 kruskal 算法(acwing858,859 / c++代码 ),算法--学习笔记,算法,图论,c++

关于重载的一些知识,,,

【第二十三课】最小生成树:prime 和 kruskal 算法(acwing858,859 / c++代码 ),算法--学习笔记,算法,图论,c++


今年就先写到这里啦。大家除夕快乐啦~

有问题欢迎指出,一起加油!!文章来源地址https://www.toymoban.com/news/detail-833774.html

到了这里,关于【第二十三课】最小生成树:prime 和 kruskal 算法(acwing858,859 / c++代码 )的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Kruskal算法求解最小生成树

    最小生成树是一个连通图。 什么是连通图,(强)连通图详解 前面介绍了《图存储结构》,本节继续讲解什么是 连通图 。 前面讲过,图中从一个顶点到达另一顶点,若存在至少一条路径,则称这两个顶点是连通着的。例如图 1 中,虽然 V1 和 V3 http://c.biancheng.net/view/3405.ht

    2024年02月07日
    浏览(54)
  • 最小生成树——Kruskal算法详解

    1.Kruskal算法解决问题 :最小生成树 2.Kruskal所需要的前提知识: 边集数组(引用)和 结构体 3.Kruskal算法主要思想: Kruskal算法将 n 个点看成 n 个独立的连通分支。 首先按边权大小排序。 然后只要在 m 条边里按 下表从小到大遍历 选出 合适 的 n - 1 条(前提条件:选出的边不

    2024年02月03日
    浏览(40)
  • 最小生成树(Prim算法,Kruskal算法)

    (1)生成树: 如果在一个无向连通图不包含回路(连通图中不存在环),则为一个树 (2)最小生成树(minimal spanning tree): 在一个图所有生成树中,代价最小的生成树称为最小生成树 (3)生成树的代价: 在一个无向连通网中,生成树各边的权值之和称为该生成树的代价

    2024年02月08日
    浏览(43)
  • 859. Kruskal算法求最小生成树

    给定一个 nn 个点 mm 条边的无向图,图中可能存在重边和自环,边权可能为负数。 求最小生成树的树边权重之和,如果最小生成树不存在则输出  impossible 。 给定一张边带权的无向图 G=(V,E)G=(V,E),其中 VV 表示图中点的集合,EE 表示图中边的集合,n=|V|n=|V|,m=|E|m=|E|。 由

    2023年04月09日
    浏览(38)
  • 图的最小生成树-Kruskal算法

    目录 问题引入  程序设计  程序分析 本节文章 【问题描述】 编写程序,利用带权无向图的邻接矩阵存储,实现图的最小生成树Kruskal算法。

    2024年02月08日
    浏览(56)
  • 最小生成树算法之Kruskal算法(c++)

    与Prim算法生成图的最小生成的树算法不同在于: Prim算法是基于图中的顶点的,且不依赖于边,Prim从顶点出发拓展,依次找每个顶点相邻的权值最小的边,直至生成最小生成树。因此,Prim算法的时间复杂度是O(v^2),适合边稠密图。 而Kruskal算法恰恰相反,是基于图中的边的一

    2024年02月12日
    浏览(41)
  • 最小生成树(Prim算法与Kruskal算法)

    一个连通图的生成树是一个极小的连通子图,它含有图中全部的n个顶点,但只有足以构成一棵树的n-1条边。我们把构造连通网的最小代价生成树称为最小生成树。 例如下图中①、②、③都是左侧图的生成树,但③是构造连通网的最小代价,所以③是该图的最小生成树。 P

    2024年02月05日
    浏览(57)
  • 最小生成树—Kruskal算法和Prim算法

    连通图:在无向图中,若从顶点v1到顶点v2有路径,则称顶点v1与顶点v2是连通的。如果图中任 意一对顶点都是连通的,则称此图为连通图。 生成树:一个连通图的最小连通子图称作该图的生成树。有n个顶点的连通图的生成树有n个顶点 和n-1条边。 最小生成树:构成生成树的

    2024年02月05日
    浏览(46)
  • 图论13-最小生成树-Kruskal算法+Prim算法

    基本思想:按照权值从小到大的顺序选择 n-1 条边,并保证这 n-1 条边不构成回路 具体做法:首先构造一个只含 n 个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中 不产生 回路,直至森林变成一棵树为止。 2.2.1 如果图不联通,直接返回空,该

    2024年02月01日
    浏览(46)
  • 最小(代价)生成树—Prim算法与Kruskal算法

    目录  一、最小生成树的特点 二、最小生成树算法  ① Prim(普里姆)算法 ②Kruskal(克鲁斯卡尔)算法  ③Prim算法与Kruskal算法对比 最小生成树是带权连通图G=(V,E)的生成树中边的权值之和最小的那棵生成树。它具有以下特点: 图G中各边权值互不相等时有唯一的最小生成树。图

    2024年02月01日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包