Prim算法简易教程(~简单易懂,附最详细注释代码)

Prim算法简易教程(~简单易懂,附最详细注释代码)详细介绍了Prim算法,图文并茂,循序渐进

大家好,又见面了,我是你们的朋友全栈君。

1 最小生成树(Minimum Spanning Tree,MST)

在一给定的无向图 G = ( V , E ) G = (V, E) G=(V,E) 中, ( u , v ) (u, v) (u,v)代表连接顶点 u u u 与顶点 v v v 的边,而 w ( u , v ) w(u, v) w(u,v) 代表此边的权重,若存在 T T T E E E 的子集且为无循环图,使得 w ( T ) w(T) w(T) 最小,则此 T T T G G G 的最小生成树,因为 T T T是由图 G G G产生的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QXLE9kV6-1597109054856)(C:\Users\剑殇\AppData\Roaming\Typora\typora-user-images\image-20200802101000918.png)]

最小生成树其实是最小权重生成树的简称。我们称求取该生成树的问题成为最小生成树问题。一个连通图可能有多个生成树。当图中的边具有权值时,总会有一个生成树的边的权值之和小于或者等于其它生成树的边的权值之和。广义上而言,对于非连通无向图来说,它的每一连通分量同样有最小生成树,它们的并被称为最小生成森林。以有线电视电缆的架设为例,若只能沿着街道布线,则以街道为边,而路口为顶点,其中必然有一最小生成树能使布线成本最低。

img

如图,这个是一个平面图,图中黑色线描述的就是最小生成树,它的权值之和小于其他的生成树。

那么,我们如何来求最小生成树呢,由最小生成树的定义我们可以知道构建最小生成树是可以利用贪心算法去实现的,我们接下来介绍的两种算法也都是利用贪心算法去求得 M S T MST MST的。因为贪心算法的策略就是在每一步尽可能多的选择中选择最优的,在当前看是最好的选择,这种策略虽然一般不能在全局中寻找到最优解,但是对于最小生成树问题来说,它的确可以找到一颗权重最小的树。

2 Prim算法

2.1 简介

普里姆算法(Prim’s algorithm),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小。该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克发现;并在1957年由美国计算机科学家罗伯特·普里姆独立发现;1959年,艾兹格·迪科斯彻再次发现了该算法。因此,在某些场合,普里姆算法又被称为DJP算法、亚尔尼克算法或普里姆-亚尔尼克算法。(来源于维基百科)

2.2 具体步骤

Prim算法是利用贪心算法实现的,我们确定根节点,从这个结点出发。普里姆算法按照以下步骤逐步扩大树中所含顶点的数目,直到遍及连通图的所有顶点。下面描述我们假设 N = ( V , E ) N=(V,E) N=(V,E)是连通网, T E TE TE N N N上最小生成树中边的集合

​ ① U = { u 0 } ( u 0 ∈ V ) , T E = { } U=\{u_0\}(u_0∈V) ,TE= \{\} U={
u0}(u0
V),TE={
}

​ ② 在所有 u ∈ U , v ∈ ( V − U ) u∈U,v∈(V-U) uU,v(VU)的边 ( u , v ) ∈ E (u,v)∈E (u,v)E找到一条权值最小的边 ( u 0 , v 0 ) (u_0,v_0) (u0,v0)并入集合 T E TE TE,同时 v 0 v_0 v0并入集合 U U U

​ ③ 重复②步骤,知道 U = V U=V U=V为止。

此时 T E TE TE中必有 n − 1 n-1 n1条边,则 T = ( V , T E ) T=(V,TE) T=(V,TE)即为我们求得的 N N N的最小生成树。

2.3 算法示例图

在这里插入图片描述

2.4 算法实现

我们如果对Dijkstra算法很熟悉的话,Prim算法也很好实现了,它们都是利用了一样的思路,但却不相同。我们用利用 l o w c o s t lowcost lowcost数组来表示到集合中最近的距离,用 c l o s e s t closest closest数组来表示最小生成树的边。怎么来表示呢?我们用顶点来形容边,也就是说我们要求的就是 c l o s e t closet closet数组。其中 c l o s e s t [ i ] closest[i] closest[i]表示的值就是与 i i i顶点相邻边的顶点序号。具体看代码(附带打印最小生成树代码)。

/* *邮箱:unique_powerhouse@qq.com *blog:https://me.csdn.net/hzf0701 *注:文章若有任何问题请私信我或评论区留言,谢谢支持。 * */
#include<bits/stdc++.h> //POJ不支持

#define rep(i,a,n) for (int i=a;i<=n;i++)//i为循环变量,a为初始值,n为界限值,递增
#define per(i,a,n) for (int i=a;i>=n;i--)//i为循环变量, a为初始值,n为界限值,递减。
#define pb push_back
#define IOS ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define fi first
#define se second
#define mp make_pair

using namespace std;

const int inf = 0x3f3f3f3f;//无穷大
const int maxn = 1e3;//最大值。
typedef long long ll;
typedef long double ld;
typedef pair<ll, ll>  pll;
typedef pair<int, int> pii;
//*******************************分割线,以上为自定义代码模板***************************************//

int n,m;//图的大小和边数。
int graph[maxn][maxn];//图
int lowcost[maxn],closest[maxn];//lowcost[i]表示i到距离集合最近的距离,closest[i]表示i与之相连边的顶点序号。
int sum;//计算最小生成树的权值总和。
void Prim(int s){ 
   
	//初始化操作,获取基本信息。
	rep(i,1,n){ 
   
		if(i==s)
			lowcost[i]=0;
		else
			lowcost[i]=graph[s][i];
		closest[i]=s;
	}
	int minn,pos;//距离集合最近的边,pos代表该点的终边下标。
	sum=0;
	rep(i,1,n){ 
   
		minn=inf;
		rep(j,1,n){ 
   
			//找出距离点集合最近的边。
			if(lowcost[j]!=0&&lowcost[j]<minn){ 
   
				minn=lowcost[j];
				pos=j;
			}
		}
		if(minn==inf)break;//说明没有找到。
		sum+=minn;//计算最小生成树的权值之和。
		lowcost[pos]=0;//加入点集合。
		rep(j,1,n){ 
   
			//由于点集合中加入了新的点,我们要去更新。
			if(lowcost[j]!=0&&graph[pos][j]<lowcost[j]){ 
   
				lowcost[j]=graph[pos][j];
				closest[j]=pos;//改变与顶点j相连的顶点序号。
			}
		}
	}
	cout<<sum<<endl;//closest数组就是我们要的最小生成树。它代表的就是边。
}
void print(int s){ 
   
	//打印最小生成树。
	int temp;
	rep(i,1,n){ 
   
		//等于s自然不算,故除去这个为n-1条边。
		if(i!=s){ 
   
			temp=closest[i];
			cout<<temp<<"->"<<i<<"边权值为:"<<graph[temp][i]<<endl;
		}
	}
}
int main(){ 
   
	//freopen("in.txt", "r", stdin);//提交的时候要注释掉
	IOS;
	while(cin>>n>>m){ 
   
		memset(graph,inf,sizeof(graph));//初始化。
		int u,v,w;//临时变量。
		rep(i,1,m){ 
   
			cin>>u>>v>>w;
			//视情况而论,我这里以无向图为例。
			graph[u][v]=graph[v][u]=w;
		}
		//任取根结点,我这里默认取1.
		Prim(1);
		print(1);//打印最小生成树。
	}
	return 0;
}



2.5 算法分析

对于此算法,我们图中有 n n n个顶点,则第一个进行初始化的循环语句的频度为 n n n,第二个循环语句的频度为 n n n但其中第二个循环中有两个内循环:第一个是在 l o w c o s t lowcost lowcost中求最小值,其频度为 n n n,第二个是重新选择具有最小权值的边,频度为 n n n,由此我们可知Prim算法的时间复杂度为 O ( n 2 ) O(n^2) O(n2),与图中的边数无关,故十分适合于稠密图。

2.6 测试

我们用示例来测试:

7 11
1 2 7
1 4 5
2 4 9
2 3 8
2 5 7
4 5 15
4 6 6
6 7 11
5 6 8
5 7 9
3 5 5

测试结果如图:
在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/158186.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • datagrip激活码【注册码】

    datagrip激活码【注册码】,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月18日
    48
  • mysql查看数据库的日志文件_怎么查看mysql数据库的日志文件「建议收藏」

    mysql查看数据库的日志文件_怎么查看mysql数据库的日志文件「建议收藏」2017-10-16回答一.错误日志错误日志在mysql数据库中很重要,它记录着mysqld启动和停止,以及服务器在运行过程中发生的任何错误的相关信息。1.配置信息–log-error=[file-name]用来指定错误日志存放的位置。如果没有指定[file-name],默认hostname.err做为文件名,默认存放在datadir目录中。也可以将log-error配置到my.cnf文件中,…

    2022年10月14日
    4
  • 如何实现微信上制作活动链接「建议收藏」

    如何实现微信上制作活动链接「建议收藏」随着互联网的快速发展,无论是房产、装修检查、家居、家店还是商城、餐饮等行业,商家们都会用到活动预约报名,线上活动链接的制作不仅成本低,而且受众也广,可以达到快速宣传的效果。相信很多小伙伴们在微信朋友圈看到的微信活动报名链接很好奇,这种活动链接是如何实现的,希望自己也可以在微信上制作这种活动链接。    工预善其事必先利其器,在这里,咱不能不提到一个非常好用的微信活动制作神器—获客宝。这款软件的神奇之处在于,他不仅可以帮你在微信上制作活动页面,而且还可以帮你侦查到谁偷偷浏览了你的页面(悄悄来,又悄悄走,不

    2026年1月18日
    5
  • 普林斯顿体系架构和哈佛架构的区别_边和 普林斯顿

    普林斯顿体系架构和哈佛架构的区别_边和 普林斯顿目前接触到的单片机架构就这两种:普林斯顿体系和哈佛结构:两者的主要区别是:codememory和datememory是不是分开存放。普林斯顿体系是程序存储器和数据存储器集合一体的架构;MEMORY单总线到CPU,这样在一个工作周期中:读指令—译码—-取数据过程中,读指令和取数据两次访问不得不分开按次序执行,效率低;特别是这样的设计使得CPU在访存时遇到了很大的瓶颈,特别是现在C

    2022年10月4日
    3
  • docker dockerfile详解_进入docker容器命令

    docker dockerfile详解_进入docker容器命令前言Dockerfile是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。Dockerfile简介Dockerfile是用来构建Docker镜像的构建文件,是由一系列

    2022年7月28日
    11
  • 服务器可以ghost备份吗_服务器可以用dism备份吗

    服务器可以ghost备份吗_服务器可以用dism备份吗带RAID服务器能GHOST备份吗?一、不可以的原因:1、从saymantec上查询到不行:Ghost与RAID的兼容性情形本文介绍Ghost与使用RAID的计算机的兼容性。解释请注意:无论驱动器使用软件级RAID还是硬件级RAID,赛门铁克都不提供制作RAID驱动器映像的技术支持。能否成功制作RAID驱动器映像取决于特定的计算机模型、驱动程序控制器、硬盘驱动器和…

    2025年9月18日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号