C++:图的最小生成树

news/2024/10/5 12:33:40/

一、简介

        连通图的生成树是包含图中全部顶点的一个极小连通子图。极小的含义是包含全部顶点的连通子图中,边数最小的子图。一颗具有n个顶点的生成树,有且仅有n-1条边。显然,生成树可能不唯一。

        无向连通网的生成树上各边的权值之和称为该生成树的代价,在图的所有生成树中,代价最小的生成树称为最小生成树

        最小生成树的概念可以应用到许多实际问题中,例如,在n个城市之间建造通信网络,至少需要假设n-1条通信线路,每两个城市之间架设通信线路的造假是不一样的,如何设计才能使得总造价最小?如果用图的顶点表示城市,用边上的权值表示建造城市之间的通信线路所需的费用,则最小生成树给出了建造通信网络的最优方案。 

         本文将介绍两种求最小生成树的算法——Prim算法Kruskal算法

二、Prim算法 

         Prim算法是用来求解无向图的最小生成树的一种贪心算法

        它的基本思想是:从一个任意节点出发,逐步选择边来连接未被访问的节点,使得当前生成树的边的权值之和最小,直到所有节点都被连接为止。

        Prim算法适用于求稠密网的最小生成树,其步骤大致如下:

  1. 选择一个起始点:选择一个任意的节点作为最小生成树的初始点,可以取第一个节点(0)。
  2. 扩展树的边:从已选节点开始,选择最小的边,并将其加入生成树。
  3. 更新权值:在加入新节点后,更新现有生成树到其他未加入节点的最小边的权值。
  4. 重复以上步骤,直到所有节点都被加入生成树。
#include <iostream>
using namespace std;
#include <vector>           //使用vector要用//图的邻接矩阵(0表示无边,非0值表示边的权重/距离)
vector<vector<int>> edge={{ 0,2,0,6,0 },{ 2,0,3,8,5 },{ 0,3,0,0,7 },{ 6,8,0,0,9 },{ 0,5,7,9,0 }};
//图的顶点个数
int vertex_num=5;//功能:找出带最小权值的边的未访问顶点
//参数:1.权值数组;2.顶点访问状态数组
int Get_min_dist(const vector<int>& dist, const vector<bool>& visited)
{int min = INT_MAX, min_index(-1);for (int i = 0; i < vertex_num; i++)   //遍历所有顶点{if (!visited[i] && dist[i] < min)  //若该顶点未被访问且权值较小{min = dist[i];  //更新最小值min_index = i;  //更新最小值对应的下标}}return min_index;       //返回最小值对应的下标
}//功能:求最小生成树
//参数:图的邻接矩阵
void primMST(vector<vector<int>> edge)
{vector<int> dist(vertex_num, INT_MAX);//存储每个节点到最小生成树的直接距离的最小值,初始值为INT_MAX,表示还未连接到树中vector<int> parent(vertex_num, -1);//存储每个节点的父节点,每个节点及其父节点构成最小生成树的一条边,初始值为-1vector<bool> visited(vertex_num, false);//用来标记一个节点是否已经包含在最小生成树中dist[0] = 0;//从节点0开始构建最小生成树//构建最小生成树for (int i = 0; i < vertex_num - 1; i++)     //执行n-1次循环,之后便可确定n-1条边{int u = Get_min_dist(dist, visited);//获得离已有生成树最近的节点的下标uvisited[u] = true;//标记该节点为已访问(加入到生成树中)for (int j = 0; j < vertex_num; j++)     //遍历每个节点,n次循环{if (edge[u][j] && !visited[j] && edge[u][j] < dist[j])//若该节点和u之间有边、未被访问、距离较小{dist[j] = edge[u][j];  //更新最小距离parent[j] = u;         //更新最小距离对应的父节点}}}//输出结果int sum(0);cout << "edge\tweight\n";for (int i = 1; i < vertex_num; i++){cout << i << "-" << parent[i] << "\t" << dist[i] << endl;sum += dist[i];          //将最小生成树的边累加起来}cout << "sum_dist:" << sum;  //总的最小距离,一般需要的是这个。
}int main()
{primMST(edge);//调用求最小生成树的函数return 0;
}

        结果如下图所示:

 三、Kruskal算法

         Kruskal算法是求解的另一种经典贪心算法

        该算法的基本思想是:通过边的权重进行排序,然后逐步将权重最小的边加入生成树。对于每条边,算法检查是否会形成环,如果不形成环,则将这条边加入生成树。这个过程持续到生成树包含所有节点为止。

        Kruskal适用于稀疏网,其大致步骤如下:

  1. 初始化

    • 将图中所有的边按权重从小到大排序。
    • 为每个节点初始化一个独立的集合,通常使用并查集(Union-Find)来高效管理这些集合。
  2. 选择边

    • 从权重最小的边开始,检查该边的两个端点是否属于同一个集合。
    • 如果不在同一集合中,说明加入这条边不会形成环,将这条边加入生成树,并合并这两个集合。
  3. 终止条件

    • 重复以上步骤,直到生成树中包含 n-1条边(其中n是图中节点的数量)。
#include <iostream>
using namespace std;
#include <algorithm>      //使用sort()排序函数要用
#include <vector>//边的结构体,用于表示边,包含起点、终点、权重
struct edge                       
{int src;int dest;int weight;
};//并查集,用于维护每个节点的父节点和秩(rank),以便高效地进行合并操作
struct subset                     
{int parent;int rank;  //秩,后续初始为0,可以理解为等级,等级越高表示树的层数越高//在树的合并时,将层数少的作为子树,并到层数高的树下
};//自定义排序规则,按权重升序排列
bool compareEdge(edge a, edge b)  
{return a.weight < b.weight;
}//用于查找某个节点的根节点,同时压缩路径以提高效率
int find(subset subsets[], int i)
{if (subsets[i].parent != i)   //若该节点的父节点不是它自己,即它不是根节点{subsets[i].parent = find(subsets, subsets[i].parent); //继续查找其根节点并压缩路径}return subsets[i].parent;                      //返回该节点的根节点
}//用于合并两个节点的集合,采用按秩合并的方法
void union_set(subset subsets[], int x, int y)
{int xroot = find(subsets, x);                  //找到x的根节点int yroot = find(subsets, y);                  //找到y的根节点if (subsets[xroot].rank < subsets[yroot].rank) //取秩较大者的集合的根节点作为合并后的根节点{subsets[xroot].parent = yroot;}else if (subsets[xroot].rank > subsets[yroot].rank){subsets[yroot].parent = xroot;}else                                          //若秩相同,则任取一方的根节点为合并后的根节点{subsets[yroot].parent = xroot;subsets[xroot].rank++;}
}//Kruskal算法实现
void Kruskal(vector<edge>& edges, int vertex_num)
{vector<edge> result;//存放结果int edge_count = 0;//记录最小生成树已有的边,达到vertex_num-1时结束sort(edges.begin(), edges.end(), compareEdge);//将所有边按权值升序排列subset* subsets = new subset[vertex_num];for (int i = 0; i < vertex_num; i++)  //为每个节点创建并查集{subsets[i].parent = i;//每个节点初始时其根节点是它自己,因为每个节点最开始都是单独一个subsets[i].rank = 0;              //同时秩(可以理解为层数)也为最低}for (edge e : edges)                  //遍历每条边{int x = find(subsets, e.src);     //找到该边起点的根节点int y = find(subsets, e.dest);    //找到该边终点的根节点if (x != y)//若俩根节点不一致,即起点和终点没有都加入生成树,加入该边不形成环,可合并{result.push_back(e);          //把该边加入生成树union_set(subsets, x, y);     //合并edge_count++;                 //边数+1}if (edge_count == vertex_num - 1) //若边数已达n-1条,退出循环,结束构建{break;}}//输出结果int sum(0);cout << "edge\tweight\n";for (edge edge : result){cout << edge.src << "-" << edge.dest << "\t" << edge.weight << endl;sum += edge.weight;}cout <<"sum_weight:"<< sum;           //一般是求这个,总的最小权重delete[] subsets;                     //释放动态内存,避免内存泄漏
}int main()
{//图的边信息vector<edge> edges = { {0, 1, 10},{0, 2, 6 },{0, 3, 5 },{1, 3, 15},{2, 3, 4 } };int vertex_num = 4;                    //顶点数 Kruskal(edges, vertex_num);            //调用Kruskal算法求最小生成树return 0;
}

        结果如图所示:


http://www.ppmy.cn/news/1534948.html

相关文章

如何使用C语言接入Doris数据库

如何使用C语言接入Doris数据库 一、环境准备1. 安装MySQL C API2. Doris数据库环境二、编写C语言接入代码1. 包含必要的头文件2. 编写连接和查询函数3. 编译和运行程序三、注意事项1. 安全性2. 错误处理3. 性能优化4. 兼容性5. 调试和日志记录四、结论Doris(之前称为Palo或Apa…

爬虫prc技术----小红书爬取解决xs

知识星球&#xff1a;知识星球 | 深度连接铁杆粉丝&#xff0c;运营高品质社群&#xff0c;知识变现的工具知识星球是创作者连接铁杆粉丝&#xff0c;实现知识变现的工具。任何从事创作或艺术的人&#xff0c;例如艺术家、工匠、教师、学术研究、科普等&#xff0c;只要能获得一…

Three.js基础内容(一)

目录 一、几何体顶点和模型 1.1、点模型对象(Points)渲染顶点数据 1.2、线模型(Line)渲染顶点数据&#xff08;画个心&#xff09; 1.3、网格模型(Mesh)渲染顶点数据(三角形概念) 1.4、构建一个矩形平面几何体 1.5、几何顶点索引数据 1.6、顶点法线数据 1.7、查看three…

Ubuntu 系统崩了,如何把数据拷下来

问题描述&#xff1a; Linux系统中安装输入法后&#xff0c;重启后&#xff0c;导致系统无法进入&#xff0c;进入 recovery mode下的resume 也启动不了&#xff0c;所以决定将需要的东西复制到U盘 解决方案&#xff1a; 1.重启ubuntu&#xff0c;随即点按Esc进入grub菜单&am…

制造企业各部门如何参与生产成本控制与管理?

​国内制造业的分量可不轻&#xff0c;从日常生活用品到高端工业设备&#xff0c;中国制造几乎涵盖了各个领域。 不过很多制造业企业在管理方面确实存在一些难题&#xff1a;成本控制不容易&#xff0c;产品质量并不稳定&#xff0c;生产周期也常常较长。 一、中国制造业生产管…

计算机毕业设计 基于爬虫与文本挖掘的网络舆情监控系统的设计与实现 Python+Django+Vue 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

【RabbitMQ】面试题

在本篇文章中&#xff0c;主要是介绍RabbitMQ一些常见的面试题。对于前几篇文章的代码&#xff0c;都已经在码云中给出&#xff0c;链接是mq-test: 学习RabbitMQ的一些简单案例 (gitee.com)&#xff0c;如果存在问题的话欢迎各位提出&#xff0c;望共同进步。 MQ的作用以及应用…

GGHead:基于3D高斯的快速可泛化3D数字人生成技术

随着虚拟现实(VR)、增强现实(AR)和数字人技术的发展,对高质量、实时生成的3D头部模型的需求日益增长。传统的3D生成方法往往依赖于复杂的2D超分辨率网络或大量的3D数据,这不仅增加了计算成本,还限制了生成速度和灵活性。为了解决这些问题,研究人员开发了一种名为GGHead…