当前位置: 首页 > news >正文

最小生成树-prim、kruskal算法

目录

 prim算法

 kruskal算法

题目练习 

(1)AcWing 858. Prim算法求最小生成树 - AcWing

 (2)859. Kruskal算法求最小生成树 - AcWing题库​编辑


学习之前建议温习一下迪杰斯特拉算法和并查集~ 

       先简单认识下最小生成树:

         最小生成树是所有节点的最小连通子图,即:以最小的成本(边的权值)将图中所有节点链接到一起。图中有n个节点,那么一定可以用n-1条边将所有节点连接到一起。那么如何选择这n-1条边就是最小生成树算法的任务所在。

        下面我们以一道模板题来讲解如何解决这个问题~~

 prim算法

        prim算法是从节点的角度采用贪心的策略每次寻找距离最小生成树最近的节点并加入到最小生成树中。prim算法核心就是三步,一定要熟悉这三步,代码相对会好些很多:

  1. 第一步,选距离生成树最近节点
  2. 第二步,最近节点加入生成树
  3. 第三步,更新非生成树节点到生成树的距离(即更新minDist数组,minDist数组用来记录每一个节点距离最小生成树的最近距离

 下面按照这三步解决一下刚刚的题目~

        ① 初始化mindist数组为 10001【默认每个节点距离最小生成树是最大的,这样后面我们在比较的时候,发现更近的距离,才能更新到minDist数组上】

        ② 选距离生成树最近节点:刚开始还没有最小生成树,所以随便选一个节点加入就好(因为每一个节点一定会在最小生成树里,所以随便选一个就好),那我们选择节点1(符合遍历数组的习惯,第一个遍历的也是节点1)----->最近节点加入生成树:  此时节点1已经算最小生成树的节点-------->更新非生成树节点到生成树的距离(即更新minDist数组,如下图)

        ③选距离生成树最近节点 :选取一个距离最小生成树(节点1)最近的非生成树里的节点,节点2,3,5距离最小生成树(节点1)最近,选节点2(其实选节点3或者节点2都可以,距离一样的)加入最小生成树---->最近节点加入生成树:此时节点1和节点2,已经算最小生成树的节点---->更新非生成树节点到生成树的距离(即更新minDist数组)\

        ④最小生成树(节点1、节点2),节点3,6距离最小生成树(节点1、节点2)最近,选节点3(选节点6也可以,距离一样)加入最小生成树----->此时节点1、节点2、节点3算是最小生成树的节点。----->更新与生成树结点相连接的非生成树节点到生成树的距离 

  • 节点4和节点3的距离为1,和原先的距离值2小,所以更新minDist[4]为1。

 .....

......

......

最后节点7加入最小生成树

        绿色的边将所有节点链接到一起,并且保证权值是最小的,因为我们在更新minDist数组的时候,都是选距离最小生成树最近 的点加入到树中 

我们要求最小生成树里边的权值总和就是把最后的minDist数组累加一起

 完整代码

#include<bits/stdc++.h>
using namespace std;
int main()
{int v,e;cin>>v>>e;//邻接矩阵存储图 1-based索引vector<vector<int>> grid(v+1,vector<int>(v+1,10001));while(e--){int x,y,k;cin>>x>>y>>k;//双向图grid[x][y]=k;grid[y][x]=k;}//所有结点到最小生成树的最小距离vector<int> mindist(v+1,10001);// 这个结点是否在树里vector<bool> isintree(v+1,false);//循环n-1次,建立n-1条边=就可以把n个结点的图连接在一起for(int i=1;i<v;i++){//1、选距离生成树最近的节点int cur=-1;int minval=INT_MAX;for(int j=1;j<=v;j++) //1 - v,顶点编号,这里下标从1开始{if(!isintree[j]&&mindist[j]<minval){minval=mindist[j];cur=j;}}//2、最近节点加入生成树isintree[cur]=true;//3、更新非生成树节点到生成树的距离(即更新minDist数组)//只需要关心与 cur 相连的非生成树节点的距离是否比原非生成树节点到生成树节点的距离更小了呢for(int j=1;j<=v;j++){if(!isintree[j]&&grid[cur][j]<mindist[j])mindist[j]=grid[cur][j];}}//统计结果int result=0;for(int i=2;i<=v;i++)//不计第一个顶点,因为统计的是边的权值,v个节点有 v-1条边{result+=mindist[i];}cout<<result;}

 真的和dijstrk求最短路径的模板很像!!!


 kruskal算法

用另外一种算法求解上题,prim 算法是维护节点的集合,而 Kruskal 是维护边的集合

kruscal的思路:

  • 边的权值排序,因为要优先选最小的边加入到生成树里
  • 遍历排序后的边
    • 如果边首尾的两个节点在同一个集合,说明如果连上这条边图中会出现环
    • 如果边首尾的两个节点不在同一个集合,加入到最小生成树,并把两个节点加入同一个集合

下面我们画图举例说明kruskal的工作过程。

开始从头遍历排序后的边

判断两个节点是否在同一个集合,就看图中两个节点是否有绿色的粗线连着就行 

.......

.......

 

 在代码中,如果将两个节点加入同一个集合,又如何判断两个节点是否在同一个集合呢

 这里就涉及到我们之前讲解的并查集:数据结构--并查集-高效处理连通性问题-CSDN博客

完整代码 

#include<bits/stdc++.h>
using namespace std;
const int N=10001;
vector<int> fa(N,-1);//并查集标记节点关系的数组
int find(int x){if(x==fa[x]) return x;else return fa[x]=find(fa[x]);
}
void merge(int x,int y)
{int xx=find(x);int yy=find(y);if(xx==yy) return ;fa[yy]=xx;
}
struct Edge
{int l,r,val;// l,r为 边两边的节点,val为边的数值//定义结构体的排序规则bool operator<(const Edge& other) const{return val < other.val;}
};
int main()
{int v,e;cin>>v>>e;vector<Edge> edges;//存储图while(e--){int v1, v2, val;cin>>v1>>v2>>val;edges.push_back({v1,v2,val});}//并查集初始化for(int i=0;i<N;i++) fa[i]=i;//执行kruskal算法int result_val=0;//1、排序sort(edges.begin(),edges.end());//从头开始遍历边for(auto edge:edges){//找出两个节点的祖先int x=find(edge.l);int y=find(edge.r);//如果祖先不同则不在一个集合if(x!=y) {result_val+=edge.val;//这条边可以计入生成树的边merge(x,y);//两个结点加入一个集合}}cout<<result_val;return 0;}

我觉得这个更简单诶 


图是稀疏图,点很多但边很少---->kruskal算法

图是稠密图,几乎每个点都和其他点有边相连,点多边多---->prim算法


题目练习 

(1)AcWing 858. Prim算法求最小生成树 - AcWing

m很大,几乎每个点都和其他点有边相连,适用Prim算法 

#include <bits/stdc++.h>
using namespace std;int main() {int n, m;cin >> n >> m;vector<vector<int>> g(n + 1, vector<int>(n + 1, 10001)); // 初始化为较大的值while (m--) {int u, v, w;cin >> u >> v >> w;if (w < g[u][v]) { // 处理重边,保留最小边权g[u][v] = w;g[v][u] = w; //无向边}}vector<int> dist(n + 1, 10001); // 存储各节点到MST的最小距离vector<bool> visited(n + 1, false);dist[1] = 0; // 起始节点初始化为0int total = 0;for (int i = 0; i < n; ++i) { // 需要选择n次,最后一次确认连通性int u = -1, minDist = 10001;for (int j = 1; j <= n; ++j) {if (!visited[j] && dist[j] < minDist) {minDist = dist[j];u = j;}}if (u == -1) { // 无法找到有效节点,图不连通cout << "impossible";return 0;}visited[u] = true;total += dist[u]; // 累加边权// 更新相邻节点的距离for (int v = 1; v <= n; ++v) {if (!visited[v] && g[u][v] < dist[v]) {dist[v] = g[u][v];}}}cout << total;return 0;
}

 (2)859. Kruskal算法求最小生成树 - AcWing题库

n这麽多,m也这麽多,图很大,适合使用 Kruskal 算法

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
vector<int> fa(N,-1);//并查集标记节点关系的数组
int find(int x){if(x==fa[x]) return x;else return fa[x]=find(fa[x]);
}
void merge(int x,int y)
{int xx=find(x);int yy=find(y);if(xx==yy) return ;fa[yy]=xx;
}
struct Edge
{int l,r,val;// l,r为 边两边的节点,val为边的数值//定义结构体的排序规则bool operator<(const Edge& other) const{return val < other.val;}
};
int main()
{int v,e;cin>>v>>e;vector<Edge> edges;//存储图while(e--){int v1, v2, val;cin>>v1>>v2>>val;edges.push_back({v1,v2,val}); //不用去重}//并查集初始化for(int i=0;i<N;i++) fa[i]=i;//执行kruskal算法int edge_count = 0; // 记录加入生成树的边数int result_val=0;//1、排序sort(edges.begin(),edges.end());//从头开始遍历边for(auto edge:edges){//找出两个节点的祖先int x=find(edge.l);int y=find(edge.r);//如果祖先不同则不在一个集合if(x!=y) {result_val+=edge.val;//这条边可以计入生成树的边merge(x,y);//两个结点加入一个集合edge_count++;if (edge_count == v - 1) break; // 提前终止:最小生成树已经完成}}if (edge_count == v - 1)cout << result_val << endl;elsecout << "impossible" << endl;return 0;}

相关文章:

  • 配置 C/C++ 语言智能感知(IntelliSense)的 c_cpp_properties.json 文件内容
  • Redis Cluster 使用 CRC16 算法实现 Slot 槽位分片的核心细节
  • git Http改用户下载
  • 直接偏好优化(Direct Preference Optimization,DPO):论文与源码解析
  • 3. pandas笔记之:创建
  • 如何在Spring Boot中配置自定义端口运行应用程序
  • Linux GPIO驱动开发实战:Poll与异步通知双机制详解
  • 方案研读:106页华为企业架构设计方法及实例【附全文阅读】
  • 在深度学习中FLOPs和GFLOPs的含义及区别
  • C语言编程--16.删除链表的倒数第n个节点
  • C# 结构(Struct)
  • 【“星睿O6”AI PC开发套件评测】开箱+刷机+基础环境配置
  • 在Ubuntu 18.04 和 ROS Melodic 上编译 UFOMap
  • 进入救援模式(物理服务器)
  • LeetCode238_除自身以外数组的乘积
  • untiy 实现点击按钮切换天空盒子
  • 手动实现legend 与 echarts图交互 通过js事件实现图标某项的高亮 显示与隐藏
  • Vivado22 Vcs18仿真联调原语缺失
  • DNS实验
  • STM32F407使用ESP8266实现阿里云OTA(中)
  • 五一假期上海路网哪里易拥堵?怎么错峰更靠谱?研判报告来了
  • 公安部知识产权犯罪侦查局:侦破盗录传播春节档院线电影刑案25起
  • 安徽铁塔回应“指挥调度中心大屏现不雅视频”:将严肃处理
  • 第六次“太空会师”,神舟二十号3名航天员顺利进驻中国空间站
  • 广东东莞调整普通住宅价格标准:一类镇街上浮300余元/平方米
  • 李良生已任应急管理部党委委员、政治部主任