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

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

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • rc522串口调试_单闭环直流调速系统实验报告

    rc522串口调试_单闭环直流调速系统实验报告RC522寻卡,防冲撞都可以,但是选卡失败是什么原因?欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML图表FLowchart流程…

  • java swt griddata_SWT中GridLayout 和GridData的使用

    java swt griddata_SWT中GridLayout 和GridData的使用1.[代码][Java]代码packagecn.haibin.rcp.test.layer;importorg.eclipse.jface.viewers.TableViewer;importorg.eclipse.swt.SWT;importorg.eclipse.swt.layout.GridData;importorg.eclipse.swt.layout.GridLayout;i…

  • Android入门教程二之开发环境搭建[通俗易懂]

    Android入门教程二之开发环境搭建[通俗易懂]不废话,直接上车:现在主流的Android开发环境有:①Eclipse+ADT+SDK②AndroidStudio+SDK③IntelliJIDEA+SDK现在国内大部分开发人员还是使用的Eclipse,而谷歌宣布不再更新ADT后,并且官网也去掉了集成Android开发环境的Eclipse下载链接,各种现象都表示开发者最后都终将过渡到AndroidStud

  • python海龟绘图画圆_Python启蒙之海龟作图「建议收藏」

    python海龟绘图画圆_Python启蒙之海龟作图「建议收藏」今天我要向大家介绍一下如何使用Python进行绘图,学会了基本绘图后,你就可以使用电脑绘制出很多漂亮的图形了,先给大家展示几幅使用Python绘图完成的精美图案吧。这副图形电脑是如何绘制出来的呢?试想一下,如果现在给你一张纸和一支笔,你如何做出这幅图形。你可以从中心点开始,然后一条条线开始绘制,直到完成最边缘的线条。电脑作图的方式就是充分模拟了你手工绘画的流程,通过程序控制了手工的作图。那既…

  • 微机原理与接口技术马春燕答案_微机原理与接口技术难不难

    微机原理与接口技术马春燕答案_微机原理与接口技术难不难spContent=课程面向有志于从事计算机过程控制系统设计、或对计算机硬件结构感兴趣的学习者。总体目标是:具备输入/输出接口控制系统软硬件初步设计能力。课程以“家庭安全防盗系统”案例引导,主要介绍:计算机基础知识、微型机基本工作原理、80×86基本指令集、汇编程序设计、存储器接口设计、接口控制技术等。——课程团队课程概述在今天的信息化时代,计算机已成为了人类工作和生活中必不可少的一部分。计算机…

  • [UVALive 6663 Count the Regions] (dfs + 离散化)[通俗易懂]

    [UVALive 6663 Count the Regions] (dfs + 离散化)

发表回复

您的电子邮箱地址不会被公开。

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