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

目录

前言

Prime算法--加点法

acwing-858 

代码如下

一些解释 

Kruskal算法--加边法

acwing-859

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

代码如下

一些解释  


前言

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

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

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

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

截取自视频。

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

Prime算法--加点法

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

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

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

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

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

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

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

方便大家理解啦~ 

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

下面是模板题:

acwing-858 

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

与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的代码放在一起对比一下

Kruskal算法--加边法

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

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

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

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

acwing-859

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

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

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

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

应该是可以理解啦。 

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

【第十四课】并查集(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 对象 。

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


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

有问题欢迎指出,一起加油!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/382847.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

拟合案例1:matlab积分函数拟合详细步骤及源码

本文介绍一下基于matlab实现积分函数拟合的过程。采用的工具是lsqcurvefit和nlinfit两个函数工具。关于包含积分运算的函数,这里可以分为两大类啊。我们用具体的案例来展示:一种是积分运算中不包含这个自变量,如下图的第一个公式,也就是说它这个积分运算只有R和Q这两个待定…

Node.js基于vue的音乐分享网站系统_6045i

随着用户听歌娱乐的需求和管理上的不断提升&#xff0c;在线音乐分享平台管理的潜力将无限扩大&#xff0c;在线音乐分享平台在业界被广泛关注&#xff0c;本网站及对此进行总体分析&#xff0c;将信息管理的发展提供参考。在线音乐分享平台对宿舍有着明显的带动效应&#xff0…

【C++】C++入门(二)

个人主页 &#xff1a; zxctscl 文章封面来自&#xff1a;艺术家–贤海林 如有转载请先通知 文章目录 1. 前言2. 缺省参数2.1 缺省参数概念2.2 缺省参数分类 3. 函数重载3.1 函数重载概念3.2 C支持函数重载的原理--名字修饰(name Mangling) 1. 前言 在前面一篇文章中简单介绍了…

(C++)集合数据文件存储工具

前言 一个简单的实现简便 "map集合" 数据存储本地。 适合不会SQL但又想实现数据存储本地的同学。 操作使用都非常简单。 文件只做了简单的加密处理&#xff0c;如果需要复杂加密的同学可以修改加密函数。 项目结构 1.创建头文件——CAB.h // // Created by xw o…

Python编程之旅:从入门到精通

在数字世界的无尽宇宙中&#xff0c;Python无疑是一颗璀璨的明星。其简洁易懂的语法、丰富的库和广泛的应用领域&#xff0c;使得Python成为了众多初学者的首选编程语言。那么&#xff0c;如何学习Python呢&#xff1f;本文将带你一步步踏上Python编程的旅程。 一、入门篇&…

本地运行多种大语言模型:一行代码即可完成 | 开源日报 No.167

ollama/ollama Stars: 33.5k License: MIT ollama 是一个轻量级、可扩展的本地语言模型构建和运行框架。 提供简单的 API 用于创建、运行和管理模型包含丰富的预构建模型库&#xff0c;方便在各种应用中使用支持从 GGUF、PyTorch 或 Safetensors 导入自定义模型可以通过命令行…

C语言rand随机数知识解析和猜数字小游戏

rand随机数 rand C语言中提供了一个可以随机生成一个随机数的函数&#xff1a;rand&#xff08;&#xff09; 函数原型&#xff1a; int rand(void);rand函数返回的值的区间是&#xff1a;0~RAND_MAX(32767)之间。大部分编译器都是32767。 #include<stdlib.h> int ma…

探索Spring Validation:优雅实现后端数据验证的艺术

在现代Web应用开发中&#xff0c;数据验证是一项至关重要的任务&#xff0c;确保应用程序接收到的用户输入符合预期规范&#xff0c;不仅能够提高系统的健壮性&#xff0c;也能有效防止潜在的安全漏洞。Spring Framework通过其内置的Spring Validation模块&#xff0c;为我们提…

中国判决生效,诺基亚全面与中国手机签署授权协议,降低专利费

日前媒体报道指诺基亚与中国两家手机企业都签署了专利授权协议&#xff0c;全面结束诉讼&#xff0c;而这一切正是在OPPO于去年底在重庆法院就OPPO与诺基亚的专利费诉讼问题&#xff0c;做出裁决之后&#xff0c;要求诺基亚按公平、公正等合理收费原则收取专利费。 这几年诺基亚…

如何从 Windows 硬盘恢复丢失或删除的照片

您是否曾经不小心删除了无法再恢复的重要照片&#xff1f;如果这是您的商务或家庭照片、婚礼或童年回忆或者亲人的照片怎么办&#xff1f; 根据我们的经验&#xff0c;用户在清理计算机以提高存储/速度时通常会遇到此类事故&#xff0c;并最终删除包含重要图片的文件夹&#x…

Linux--基础开发工具篇(2)(vim)(配置白名单sudo)

目录 前言 1. vim 1.1vim的基本概念 1.2vim的基本操作 1.3vim命令模式命令集 1.4vim底行命令 1.5 异常问题 1.6 批量注释和批量去注释 1.7解决普通用户无法sudo的问题 1.8简单vim配置 前言 在前面我们学习了yum&#xff0c;也就是Linux系统的应用商店 Linux--基础开…

专业135+总400+中国科学院大学859国科大信号与系统考研经验电子信息与通信,真题,大纲,参考书

今年考研专业课859信号与系统135&#xff0c;总分400上岸国科大&#xff0c;总结一下自己这一年的复习经验&#xff0c;希望对后面报考中科院大学的同学有所帮助。 专业课&#xff1a; 国科大不同研究所都是统一命题&#xff0c;859信号与系统的参考书目是郑君里的《信号与系…

windows 下安装gin

go install 执行命令&#xff0c;执行不了的参考一下 https://blog.csdn.net/weixin_42592326/article/details/135946806 Golang 中没法下载第三方包解决办法-CSDN博客 go install github.com/gin-gonic/ginlatest 还是安装不了的话&#xff0c;用手机开热点&#xff0c;电…

73. 矩阵置零(Java)

目录 题目描述&#xff1a;输入&#xff1a;输出&#xff1a;代码实现&#xff1a; 题目描述&#xff1a; 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 输入&#xff1a; matrix [[1,1,1],[1,0,…

react函数组件中使用context

效果 1.在父组件中创建一个createcontext并将他导出 import React, { createContext } from react import Bpp from ./Bpp import Cpp from ./Cpp export let MyContext createContext(我是组件B) export let Ccontext createContext(我是组件C)export default function App…

Hive正则表达式

Hive版本&#xff1a;hive-3.1.2 一、Hive的正则表达式概述 正则表达式是一种用于匹配和操作文本的强大工具&#xff0c;它是由一系列字符和特殊字符组成的模式&#xff0c;用于描述要匹配的文本模式。 Hive的正则表达式灵活使用解决HQL开发过程中的很多问题&#xff0c;本篇文…

08-OpenFeign-结合Sentinel,实现熔断降级

当我们在对服务远程调用时&#xff0c;会因为服务的请求超时、抛出异常等情况&#xff0c;导致调用失败。 如果短时间内&#xff0c;产生大量请求异常。引发上游的调用方请求积压&#xff0c;最终会引起整个调用链雪崩。 为此我们需要对核心的调用过程进行监控&#xff0c;当…

自适应二次元404页面源码

自适应二次元404页面源码&#xff0c;HTMLCSSJS,喜欢二次元的朋友可以下载使用 蓝奏云&#xff1a;https://wfr.lanzout.com/iuPNQ1ns7dxg

数据结构——6.2 图的存储与基本操作

6.2 图的存储与基本操作 概念 图的存储 邻接矩阵存有向图和无向图 根据邻接矩阵求度&#xff1a; 无向图&#xff1a;第i个结点的度 第i行 (或第列) 的非零元素个数 有向图&#xff1a; 第i个结点的出度 第i行的非零元素个数 第i个结点的入度 第i列的非零元素个数 第i个结…

刘谦春晚纸牌魔术背后的数学—海明码原理简介

在昨天2024年的春晚舞台上&#xff0c;魔术大师刘谦以一场令人拍案叫绝的纸牌魔术再度震撼全场。他巧妙地利用了数学原理&#xff0c;精准无误地让观众“随机”选择的纸牌完成了配对&#xff0c;尤其是令人忍俊不禁的是主持人尼格买提的纸牌却没有如愿配对&#xff0c;小尼碎了…