今天才写了prim的堆优化,发现kruskal居然比prim跑得快。。。


回归正题:
以下是我个人对最小生成树各种算法的理解,以及我的代码。
以下我将点数称为n,边数称为m;


Prim
算法过程(来自百度百科):
1. 输入:一个加权连通图,其中顶点集合为V,边集合为E;
2. 初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3. 重复下列操作,直到Vnew = V:
- a.在集合E中选取权值最小的边< u, v >,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
- b.将v加入集合Vnew中,将< u, v >边加入集合Enew中;
4. 输出:使用集合Vnew和Enew来描述所得到的最小生成树。
然后以下是我自己的解释:
初始时任选一个起点,并加入点集;
不断将离访问过的点集最近的未访问的点连边(或者说选最短的边),加入点集中,并将连边加入边集(或累加答案);
这样进行n-1次,每次加入一个点和一条边,结束后就会形成一棵n个节点,n-1条边的树;
正确性我也懒得证了,自己百度看吧。
时间复杂度:O(nm)
代码就不用看了,这个复杂度完全无法接受,必须优化;


Prim–堆优化
对于上面的过程,最有优化余地的一步是什么呢?

不断将离访问过的点集最近的未访问的点连边(或者说选最短的边

很明显,粗体字部分可以优化(一般来说,将求最小值或最大值从n优化至logn级别都是常见的优化);
具体方法就是将有可能扩展的边都放入一个小根堆中,每次取最小的,这样可以将每次取最小值的过程优化至log级别的;(不知道堆的同学可以自行百度)
时间复杂度:低于O(nlogm)
手写堆又会增加编程复杂度,我们可以使用c++自带的优先队列(priority_queue),这就是一个堆;

priority_queue< T > (T为类型)(后面括号内为时间复杂度)
1. T top(void):返回堆顶(即最值);(1)
2. void pop(void):弹出堆顶;(logn)
3. void push(T):压入新元素;(logn)
4. bool empty(void):如果堆为空返回true,否则返回false;(1)
5. int size(void):返回堆的元素数量;(1)
另一种定义方式:(建议)
priority_queue< T , vector< T > ,less< T > > (大根堆)
priority_queue< T , vector< T > ,greater< T > > (小根堆)
用自定义类型的前提是那个类型有定义小于号(大根堆)/ 大于号(小根堆)
关于比较符号定义可以看我的代码;


我的代码:

//此题来自洛谷P3366 【模板】最小生成树,是一道模板题,大家可以去做一下。
//我这里存边用的是邻接表,省空间,也方便;
//也可以用vector来存边
#include<bits/stdc++.h>
using namespace std;
struct edge{ //自定义边类int to,next,w;bool operator > (const edge& y) const { //大于号return w>y.w;}
}e[400001];
int n,m,tot;
int head[5001]; //邻接表用
int vis[5001]; //标记点是否访问过
priority_queue<edge,vector<edge>,greater<edge> > q; //小根堆
void addedge(int x,int y,int l){ //加边tot++;e[tot].to=y;e[tot].next=head[x];e[tot].w=l;head[x]=tot;
}
int prim(){ //我这里将1作为起点for(int i=head[1];i;i=e[i].next){ //将1的邻边都放入优先队列q.push(e[i]);}vis[1]=1;int ans=0;int left=n-1; //剩余需要加边数while(left&&!q.empty()){edge t=q.top();q.pop();if(vis[t.to]){continue;}ans+=t.w;left--;int u=t.to;vis[u]=1;for(int i=head[u];i;i=e[i].next){int v=e[i].to;if(!vis[v]){q.push(e[i]);}}}return ans;
}
int main(){scanf("%d %d",&n,&m);for(int i=1;i<=m;i++){int x,y,l;scanf("%d %d %d",&x,&y,&l);addedge(x,y,l);addedge(y,x,l);}int ans=prim();printf("%d",ans);return 0;
}

Kruskal
算法过程(来自百度百科):
先构造一个只含 n 个顶点、而边集为空的子图,把子图中各个顶点看成各棵树上的根结点,之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,即把两棵树合成一棵树,反之,若该条边的两个顶点已落在同一棵树上,则不可取(会出现环),而应该取下一条权值最小的边再试之。依次类推,直到森林中只有一棵树,也即子图中含有 n-1 条边为止。
我的描述:
将边排序后,从小到大只要不形成环就加边,直到加够n-1条边为止。
正确性很显然,可以自己去看一下具体证明。
时间复杂度(暴力判环):低于O(m^2)
很明显,判环这里可以用并查集优化到近似O(1),那么算法的时间就都会集中在对边进行排序上,时间也就是排序的时间;
时间复杂度(并查集判环):O(mlogm)
这样的复杂度在稀疏图优于Prim,稠密图劣于Prim,自己酌情使用。
Kruskal的好处在于它极易编写,而Prim编写难度较高。


下来看代码吧:

//这题也是洛谷P3366,大家可以再写一次Kruskal交一次。
#include<bits/stdc++.h>
#define inf 2000000000
using namespace std;
struct edge{int a,b,w;
}e[200001];
int n,m;
int f[5001];
int sum=0;
int find(int x){ //并查集return x==f[x]?x:f[x]=find(f[x]);
}
void add(int i,int x,int y,int l){e[i].w=l;e[i].a=x;e[i].b=y;
}
int cmp(edge x,edge y){return x.w<y.w;
}
int main(){cin>>n>>m;for(int i=1;i<=n;i++){f[i]=i;}for(int i=1;i<=m;i++){int x,y,l;scanf("%d %d %d",&x,&y,&l);add(i,x,y,l);}sort(e+1,e+m+1,cmp);int tot=0;//Kruskal的主体只有这一个循环for(int i=1;i<=m&&tot<=n-1;i++){if(find(e[i].a)!=find(e[i].b)){ //判环sum+=e[i].w;f[find(e[i].a)]=find(e[i].b);tot++;}}if(tot==n-1){cout<<sum;}else{cout<<"orz";}return 0;
}

交了之后不出意外应该是Kruskal略快于Prim,这是因为这题m较小。
不过,Kruskal一般情况都足够了,具体看数据范围。
以上便是我对最小生成树的总结。
end

转载于:https://www.cnblogs.com/stone41123/p/7581292.html

图论--最小生成树总结(PrimKruskal)相关推荐

  1. 牛客刷题之图论-最小生成树

    题目集链接 目录 1.黑暗城堡 2.北极通讯网络 3.新的开始 4.构造完全图 5.秘密的牛奶运输 6.Tree 7.最小生成树计数 8.次小生成树 1.黑暗城堡 先求出最小生成树的每个边权,然后枚举 ...

  2. 0x62.图论 - 最小生成树

    目录 一.KruskalKruskalKruskal算法 1.P3366 [模板]最小生成树 二.PrimPrimPrim算法 2.P2212 [USACO14MAR]Watering the Fie ...

  3. 【uva 1395】Slim Span(图论--最小生成树+结构体快速赋值 模版题)

    题意:给一个N(N<=100)个点的联通图(无自环和平行边),求苗条度(最大边-最小边的值)尽量小的生成树. 解法:枚举+Kruskal.先从小到大排序边,枚举选择的最小的边. 1 #inclu ...

  4. 模板 - 图论 - 最小生成树

    模板题啦. 虽然Prim在解决某些特殊情形时有用,但一般Kruskal就够了. #include<bits/stdc++.h> using namespace std; #define l ...

  5. 【数据结构和算法】图论—最小生成树,普里姆算法(Prim)

  6. kuangbin 最小生成树专题 - ZOJ - 1586 QS Network (朴素 Prim算法 模板题)

    kuangbin 最小生成树专题 - ZOJ - 1586 QS Network (朴素 Prim算法 模板题) 总题单 week 3 [kuangbin带你飞] 题单 最小生成树 + 线段树 Cli ...

  7. 【老生谈算法】matlab实现Kruskal避圈算法求最小生成树——Kruskal避圈算法

    基于MATLAB的Kruskal避圈算法求最小生成树 1.原文下载: 本算法原文如下,有需要的朋友可以点击进行下载 序号 原文(点击下载) 本项目原文 [老生谈算法]基于MATLAB的Kruskal避 ...

  8. NOIP 好题推荐(DP+搜索+图论)POJ ZOJ

    NOIP好题推荐(DP+搜索+图论)POJ ZOJ 1370 Gossiping (数论->模线性方程有无解的判断)+(图论->DFS)  1090 Chain ->格雷码和二进制码 ...

  9. 最小生成树python算法实践

    为了工作学习中防止忘记,特记下如此笔记,以供查阅. 最近在学图论,涉及到了最小生成树算法. 参考了两篇文章 并集查询 图论最小生成树 原代码复制后格式破坏,特意贴一下我清理后的python代码 # 构 ...

最新文章

  1. Ubuntu apt-get install、apt-get -f install、apt-get --purge remove、apt-get update、apt-get upgrade、
  2. 【动态规划、计算几何】最优三角剖分
  3. ubuntu 配置 jdk 环境
  4. 对大量转载贴识别算法的研究
  5. java猜拳游戏代码_Java实现简单猜拳游戏
  6. ubuntu+idea intellij配置android开发环境
  7. .net core 注入机制与Autofac
  8. Oracle导入dmp文件报12504,ORA-12504:TNS :监听程序在 CONNECT_DATA 中未获得SERVICE_NAME...
  9. SHELL中的引用之引用变量
  10. Honeywell1900霍尼韦尔 扫描二维码 QT 客户端显示数据
  11. 5369. 统计作战单位数
  12. python爬虫beautifulsoup爬取小说_Python3网络爬虫(七):使用Beautiful Soup爬取小说
  13. 岗位、职位、职级、职务的区别
  14. TeXmacs对中文的支持
  15. python django(1170, BLOB/TEXT column 'name' used in key specification without a key length)
  16. css图片菜鸟教程,css 常用样式(分享)
  17. Resco MobileForms Toolkit 2010的破解
  18. A15.从零开始前后端react+flask - 将前后端联系起来
  19. 华为百度导航Sdk黑屏
  20. python实现在线翻译

热门文章

  1. 协程学习-python
  2. java判断是否失效_java – 如何在输入有效之前检查无效输入和循环?
  3. 谷歌为什登不上去github_同声传译被攻陷!谷歌发布Translatotron直接语音翻译系统...
  4. idea中ssm集成freemark_基于SSM框架的迷你天猫商城
  5. plc维修入门与故障处理实例_13个浮筒液位计维修实例助你快速解决现场故障问题...
  6. springboot 添加 lombok 报错更新 版本号
  7. 2接口详解_冯博琴微型计算机原理与接口技术第3版答案资料配套题库名校考研真题课后习题章节题库模拟试题...
  8. 三菱mode bus tcp通讯_绍兴三菱MR-J4-70B
  9. groupadd r mysql_MySQL三种安装方式大揭秘
  10. linux 复制文件_使用 rsync 复制大文件的一些误解 | Linux 中国