[算法与数据结构] - No.9 图论(2)- 最小生成树Prim算法与Kruskal算法

2024-04-12 14:38

本文主要是介绍[算法与数据结构] - No.9 图论(2)- 最小生成树Prim算法与Kruskal算法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

普里姆算法的基本思想:
    从连通网络 N = { V, E }中的某一顶点 u0 出发, 选择与它关联的具有最小权值的边 ( u0, v ), 将其顶点加入到生成树顶点集合U中。以后每一步从一个顶点在 U 中,而另一个顶点不在 U 中的各条边中选择权值最小的边(u, v), 把它的顶点加入到集合 U 中。如此继续下去, 直到网络中的所有顶点都加入到生成树顶点集合 U 中为止。

如图所示:

算法实现:

设初始总的顶点集合为V,已加入最小生成树的集合点为S

定义两个辅助数组:lowcost[] 和 nextvex[],分别用来记录下标为i的到S中点最近的距离。每一次选取不在S中,同时距离S最近的点vi。将vi加入S中,同时在lowcost

中更新所有不在S中的, 与vi相连的点vj到S的最近距离。更新nextvex中vj下标的值为vi,即nextvex[vj] = vi

#include <iostream>
#include <stdlib.h>
using namespace std;
const int MAX =99999999;
int main()
{unsigned int n,e;cin>>n>>e;if(n>0&&e>0){int arr[100][100];int nextvex[100] = {0};nextvex[0] = -1;int lowcost[100] = {0};for(int i=0; i<100; i++){for(int j=0; j<100; j++){if(i!=j)arr[i][j] = MAX;elsearr[i][j] = 0;}}for(int i=0; i<e; i++){int from,to,dis;cin>>from>>to>>dis;arr[from][to] = dis;arr[to][from] = dis;//arr[to][from] = dis;}for(int j =0;j<n;j++){lowcost[j] = arr[0][j];}/*for(int i=0; i<n; i++){for(int j=0; j<n; j++){cout<<arr[i][j]<<" ";}cout<<endl<<endl;}*/int cnt = 1;while(cnt<n){int temp = MAX;int vertex;for(int k = 0;k<n;k++){if(nextvex[k]!=-1&&lowcost[k]<temp){temp = lowcost[k];vertex = k;}}cout<<nextvex[vertex]<<"->"<<vertex<<":"<<temp<<endl;nextvex[vertex] = -1;for(int l = 0;l<n;l++){if(arr[vertex][l]<MAX&&nextvex[l]!=-1){lowcost[l] = arr[vertex][l];nextvex[l] = vertex;}}cnt = cnt + 1;}}return 0;
/*
7 9
0 1 28
0 5 10
1 2 16
1 6 14
2 3 12
3 4 22
3 6 18
4 5 25
4 6 24*/
}


Kruskal算法基本思想:

j将所有的边排序。将所有的边加入集合S,即一开始S = E  。从S中拿出一条最短的边(u,v),如果(u,v)不在同一棵树内,则连接u,v合并这两棵树,同时将(u,v)加入生成树的边集E'  。重复直到所有点属于同一棵树,边集E'就是一棵最小生成树

这个算法的思想很简单,但是其中有个地方值得注意。那就是如何判断两个点是否处于同一个连通分量(同一棵树里),这里用到了并查集:

并查集:当我们需要判断某两个元素是否属于统一集合(这里是树),在数据量比较大的时候,我们用到了并查集。并查集中有两个重要的函数(初始化函数除外),按秩合并和路径压缩

初始化:

typedef struct Edge{int from;int to;int cost;
}Edge;
Edge edgeList[100];int nodeRank[100];
int nodeFather[100];
void Init(int n)
{for(int i = 0 ;i<n;i++){nodeRank[i] = 0;nodeFather[i] = i;}
}
首先,我们定义一个边的结构体。这个结构体里面定义了每个边的两个端点。同时记录这条边的长度。在初始化的函数中,我们设置每个节点的父节点是自身,同时每个节点的秩都是0。

路径压缩:

int findFather(int x)
{int root = x;while(nodeFather[root]!=root){root = nodeFather[root];}while(x!=root){int father = nodeFather[x];nodeFather[x] = root;x = father;}return root;
}
在我们查找两个节点是否处于同一个树的时候,我们的方法是不断找当前节点的父节点,查看其父节点是否为自身。如果是,该节点为该树的根;否则迭代该过程。但是,随着树的增加,每次查找父节点会很消耗时间。所以我们每次查找父节点的时候,都将除根节点以外所有节点的父节点设为根节点,方便下次查找。

按秩合并:

int unite(int x,int y)
{int fatherx = findFather(x);int fathery = findFather(y);if(nodeRank[fatherx]>nodeRank[fathery]){nodeFather[fathery] = fatherx;}else{nodeFather[fatherx] = fathery;if(nodeRank[fatherx] == nodeRank[fathery]){nodeRank[fathery]++;}}
}

如果两个子节点不在同一树里边,那么我们设置秩(我们这里是根节点的索引大小)较小的根指向秩比较大的根。

当最小生成树中的边的个数达到n-1的时候,算法结束

完整代码如下:

#include <iostream>
#include <stdlib.h>
using namespace std;
typedef struct Edge{int from;int to;int cost;
}Edge;
Edge edgeList[100];
int cmp(const void*first,const void*second)
{return ((Edge*)first)->cost - ((Edge*)second)->cost;
}
int nodeRank[100];
int nodeFather[100];
void Init(int n)
{for(int i = 0 ;i<n;i++){nodeRank[i] = 0;nodeFather[i] = i;}
}
int findFather(int x)
{int root = x;while(nodeFather[root]!=root){root = nodeFather[root];}while(x!=root){int father = nodeFather[x];nodeFather[x] = root;x = father;}return root;
}int unite(int x,int y)
{int fatherx = findFather(x);int fathery = findFather(y);if(nodeRank[fatherx]>nodeRank[fathery]){nodeFather[fathery] = fatherx;}else{nodeFather[fatherx] = fathery;if(nodeRank[fatherx] == nodeRank[fathery]){nodeRank[fathery]++;}}
}void Kruskal(int n,int e)
{int cnt = 0;qsort(edgeList,e,sizeof(Edge),cmp);/*for(int i = 0 ; i <e;i++){cout<<edgeList[i].from<<" "<<edgeList[i].to<<" "<<edgeList[i].cost<<endl;}*/for(int i = 0 ; i<e&&cnt!=n-1;i++){if(findFather(edgeList[i].from)!=findFather(edgeList[i].to)){cout<<edgeList[i].from<<" "<<edgeList[i].to<<endl;unite(edgeList[i].from,edgeList[i].to);cnt++;}}
}
/*
//一种优美的递归形式
int find(int x)
{if(x!=father[x]){father[x] = find(father[x]);}return father[x];
}
*/
int main()
{cout<<"请输入顶点数和边数:"<<endl;int n,e;cin>>n>>e;for(int i = 0 ; i <e;i++){cin>>edgeList[i].from>>edgeList[i].to>>edgeList[i].cost;}Init(n);Kruskal(n,e);return 0;/*
7 9
0 1 28
0 5 10
1 2 16
1 6 14
2 3 12
3 4 22
3 6 18
4 5 25
4 6 24*/
}

P.S. 文章不妥之处还望指正

这篇关于[算法与数据结构] - No.9 图论(2)- 最小生成树Prim算法与Kruskal算法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/897417

相关文章

Java中的雪花算法Snowflake解析与实践技巧

《Java中的雪花算法Snowflake解析与实践技巧》本文解析了雪花算法的原理、Java实现及生产实践,涵盖ID结构、位运算技巧、时钟回拨处理、WorkerId分配等关键点,并探讨了百度UidGen... 目录一、雪花算法核心原理1.1 算法起源1.2 ID结构详解1.3 核心特性二、Java实现解析2.

Python实现自动化Word文档样式复制与内容生成

《Python实现自动化Word文档样式复制与内容生成》在办公自动化领域,高效处理Word文档的样式和内容复制是一个常见需求,本文将展示如何利用Python的python-docx库实现... 目录一、为什么需要自动化 Word 文档处理二、核心功能实现:样式与表格的深度复制1. 表格复制(含样式与内容)2

python如何生成指定文件大小

《python如何生成指定文件大小》:本文主要介绍python如何生成指定文件大小的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录python生成指定文件大小方法一(速度最快)方法二(中等速度)方法三(生成可读文本文件–较慢)方法四(使用内存映射高效生成

Maven项目中集成数据库文档生成工具的操作步骤

《Maven项目中集成数据库文档生成工具的操作步骤》在Maven项目中,可以通过集成数据库文档生成工具来自动生成数据库文档,本文为大家整理了使用screw-maven-plugin(推荐)的完... 目录1. 添加插件配置到 pom.XML2. 配置数据库信息3. 执行生成命令4. 高级配置选项5. 注意事

MybatisX快速生成增删改查的方法示例

《MybatisX快速生成增删改查的方法示例》MybatisX是基于IDEA的MyBatis/MyBatis-Plus开发插件,本文主要介绍了MybatisX快速生成增删改查的方法示例,文中通过示例代... 目录1 安装2 基本功能2.1 XML跳转2.2 代码生成2.2.1 生成.xml中的sql语句头2

使用雪花算法产生id导致前端精度缺失问题解决方案

《使用雪花算法产生id导致前端精度缺失问题解决方案》雪花算法由Twitter提出,设计目的是生成唯一的、递增的ID,下面:本文主要介绍使用雪花算法产生id导致前端精度缺失问题的解决方案,文中通过代... 目录一、问题根源二、解决方案1. 全局配置Jackson序列化规则2. 实体类必须使用Long封装类3.

使用Python自动化生成PPT并结合LLM生成内容的代码解析

《使用Python自动化生成PPT并结合LLM生成内容的代码解析》PowerPoint是常用的文档工具,但手动设计和排版耗时耗力,本文将展示如何通过Python自动化提取PPT样式并生成新PPT,同时... 目录核心代码解析1. 提取 PPT 样式到 jsON关键步骤:代码片段:2. 应用 JSON 样式到

SpringBoot实现二维码生成的详细步骤与完整代码

《SpringBoot实现二维码生成的详细步骤与完整代码》如今,二维码的应用场景非常广泛,从支付到信息分享,二维码都扮演着重要角色,SpringBoot是一个非常流行的Java基于Spring框架的微... 目录一、环境搭建二、创建 Spring Boot 项目三、引入二维码生成依赖四、编写二维码生成代码五

Android与iOS设备MAC地址生成原理及Java实现详解

《Android与iOS设备MAC地址生成原理及Java实现详解》在无线网络通信中,MAC(MediaAccessControl)地址是设备的唯一网络标识符,本文主要介绍了Android与iOS设备M... 目录引言1. MAC地址基础1.1 MAC地址的组成1.2 MAC地址的分类2. android与I

Springboot实现推荐系统的协同过滤算法

《Springboot实现推荐系统的协同过滤算法》协同过滤算法是一种在推荐系统中广泛使用的算法,用于预测用户对物品(如商品、电影、音乐等)的偏好,从而实现个性化推荐,下面给大家介绍Springboot... 目录前言基本原理 算法分类 计算方法应用场景 代码实现 前言协同过滤算法(Collaborativ