最小生成树:

  一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。简单来说就是有且仅有n个点n-1条边的连通图。

  而最小生成树就是最小权重生成树的简称,即所有边的权值之和最小的生成树。

  最小生成树问题一般有以下两种求解方式。

一、Prim算法

  参考了Feynman的博客 

  Prim算法通常以邻接矩阵作为储存结构。

  算法思路:以顶点为主导地位,从起始顶点出发,通过选择当前可用的最小权值边把顶点加入到生成树当中来:

  1.从连通网络N={V,E}中的某一顶点U0出发,选择与它关联的具有最小权值的边(U0,V),将其顶点加入到生成树的顶点集合U中。

  2.以后每一步从一个顶点在U中,而另一个顶点不在U中的各条边中选择权值最小的边(U,V),把它的顶点加入到集合U中。如此继续下去,直到网络中的所有顶点都加入到生成树顶点集合U中为止。

  模板题链接:Prim算法求最小生成树

  朴素版时间复杂度O(n²)算法模板:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = +;
int n,m;
int g[N][N],dis[N],vis[N]; void prim()
{
memset(dis,0x1f,sizeof dis);
dis[]=;
for(int j=;j<=n;j++)
{
int min_len=2e+,k;
for(int i=;i<=n;i++)
{
if(!vis[i]&&dis[i]<min_len)
{
min_len=dis[i];
k=i;
}
}
vis[k]=;
for(int i=;i<=n;i++)
{
if(!vis[i]&&dis[i]>g[k][i])
dis[i]=g[k][i];
}
} } int main()
{
scanf("%d%d",&n,&m);
memset(g,0x1f,sizeof g);
for(int i=;i<=m;i++)
{
int u,v,w;scanf("%d%d%d",&u,&v,&w);
g[u][v]=g[v][u]=min(g[u][v],w); //因为有重边,所以取min
}
prim();
int ans=;
for(int i=;i<=n;i++)ans+=dis[i];
if(ans>1e7)printf("impossible\n");
else printf("%d\n",ans);
return ;
}

  与Dijkstra类似,Prim算法也可以用堆优化,优先队列代替堆,优化的Prim算法时间复杂度O(mlogn)模板(图的存储方式为前向星):

void Prim_heap(int point)
{
memset(dis,0x1f,sizeof(dis));
priority_queue<pair<int,int> > q; dis[point]=;
q.push(make_pair(,));
while(!q.empty())
{
int k=q.top().second;
q.pop();
v[k]=;
for(int i=h[k];i!=-;i=edge[i].next)
{
int to=edge[i].to,w=edge[i].w;
if(!v[to]&&dis[to]>w)
{
dis[to]=w;
q.push(make_pair(-dis[to],to)); //优先队列大根堆变小根堆小骚操作:只需一个‘-’号;
}
}
}
for(int i=;i<=n;i++)if(dis[i]==0x1f1f1f1f)flag=false; //判断是否不存在最小生成树
return ;
}

二、Kruskal算法

  相比于Prim算法,更常用的还是Kruskal,其原因在于Kruskal算法模板的代码量小而且思路易理解。

  算法思路:先构造一个只含 n 个顶点、而边集为空的子图,把子图中各个顶点看成各棵树上的根结点,之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,即把两棵树合成一棵树,反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直到森林中只有一棵树,也即子图中含有 n-1 条边为止。

  步骤:

  1. 新建图G,G中拥有原图中相同的节点,但没有边;
  2. 将原图中所有的边按权值从小到大排序;
  3. 从权值最小的边开始,如果这条边连接的两个节点于图G中不在同一个连通分量中,则添加这条边到图G中;
  4. 重复3,直至图G中所有的节点都在同一个连通分量中。

  简单来说就是以边为主导地位,每次选择权值最小的边,判断该边连接的两点是否连通,若不连通,则合并两点(合并操作以并查集实现)。记录合并的次数,当次数等于n-1时结束。

  模板题链接:Kruskal算法求最小生成树

  代码如下:时间复杂度O(mlogm)

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = +, M = +; struct Edge{
int u,v,w;
bool operator < (const Edge &E)const
{
return w<E.w;
}
}edge[M];
int fa[N];
int n,m,cnt,ans; int find(int x)
{
if(fa[x]==x)return x;
else return fa[x]=find(fa[x]);
} int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)fa[i]=i;
for(int i=;i<=m;i++)
{
int a,b,c;scanf("%d%d%d",&a,&b,&c);
edge[i].u=a;edge[i].v=b;edge[i].w=c;
}
sort(edge+,edge+m+);
for(int i=;i<=m;i++)
{
int u=find(edge[i].u),v=find(edge[i].v),w=edge[i].w;
if(u!=v)
{
cnt++;
fa[u]=v;
ans+=w;
}
}
if(cnt==n-)printf("%d\n",ans);
else printf("impossible\n");
return ;
}

三、Prim,Prim_heap,Kruskal算法时间复杂度比较

  参考了G机器猫的博客

结论:

  1.Prim在稠密图中比Kruskal优,在稀疏图中比Kruskal劣。

  2.Prim_heap在任何时候都有令人满意的的时间复杂度,但是代价是空间消耗极大。(以及代码很复杂>_<)

  但值得说一下的是,时间复杂度并不能反映出一个算法的实际优劣。

  竞赛题一般给的都是稀疏图,选择Prim_heap即可;如果觉得代码量太大,想要在Prim与Kruskal算法中选一个,那就选择Kruskal算法。

图论——最小生成树:Prim算法及优化、Kruskal算法,及时间复杂度比较的更多相关文章

  1. 【数据结构】 最小生成树(四)——利用kruskal算法搞定例题×3+变形+一道大水题

    在这一专辑(最小生成树)中的上一期讲到了prim算法,但是prim算法比较难懂,为了避免看不懂,就先用kruskal算法写题吧,下面将会将三道例题,加一道变形,以及一道大水题,水到不用高级数据结构,建 ...

  2. 算法起步之Kruskal算法

    原文:算法起步之Kruskal算法 说完并查集我们接着再来看这个算法,趁热打铁嘛.什么是最小生成树呢,很形象的一个形容就是铺自来水管道,一个村庄有很多的农舍,其实这个村庄我们可以看成一个图,而农舍就是 ...

  3. 算法8-4:Kruskal算法

    Kruskal算法用于计算一个图的最小生成树.这个算法的过程例如以下: 依照边的权重从小到达进行排序 依次将每条边添加到最小生成树中,除非这条边会造成回路 实现思路 第一个步骤须要对边进行排序,排序方 ...

  4. 算法笔记_066:Kruskal算法详解(Java)

    目录 1 问题描述 2 解决方案 2.1 构造最小生成树示例 2.2 伪码及时间效率分析 2.3 具体编码(最佳时间效率)   1 问题描述 何为Kruskal算法? 该算法功能:求取加权连通图的最小 ...

  5. 图论--最小生成树--Prim算法(带边输出)模板

    #include <bits/stdc++.h> using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 100 ...

  6. 并查集及其简单应用:优化kruskal算法

    并查集是一种可以在较短的时间内进行集合的查找与合并的树形数据结构 每次合并只需将两棵树的根合并即可 通过路径压缩减小每颗树的深度可以使查找祖先的速度加快不少 代码如下: int getfather(i ...

  7. 图论——最小生成树prim+邻接表+堆优化

    今天学长对比了最小生成树最快速的求法不管是稠密图还是稀疏图,prim+邻接表+堆优化都能得到一个很不错的速度,所以参考学长的代码打出了下列代码,make_pair还不是很会,大体理解的意思是可以同时绑 ...

  8. 最小生成树 (Minimum Spanning Tree,MST) --- Kruskal算法

    本文链接:http://www.cnblogs.com/Ash-ly/p/5409265.html 引导问题: 假设要在N个城市之间建立通信联络网,则连通N个城市只需要N - 1条线路.这时,自然会考 ...

  9. Electrification Plan 最小生成树(prim+krusl+堆优化prim)

    题目 题意: 无向图,给n个城市,n*n条边,每条边都有一个权值 代表修路的代价,其中有k个点有发电站,给出这k个点的编号,要每一个城市都连到发电站,问最小的修路代价. 思路: prim:把发电站之间 ...

随机推荐

  1. 清北学堂-DAY2-数论专题-中国剩余定理(CRT)

    首先请看定义:(百科上抄下来的)孙子定理是中国古代求解一次同余式组(见同余)的方法.是数论中一个重要定理.又称中国余数定理. 一元线性同余方程组问题最早可见于中国南北朝时期(公元5世纪)的数学著作&l ...

  2. spring cloud Eureka 配置信息

    Eureka instance 一个服务,如:订单系统,会部署多台服务器,而每台服务器上提供的服务就是instance; 负载配置. Eureka service 指的是服务,提供一种特定功能的服务, ...

  3. ThinkPHP5.0.*远程代码执行漏洞预警

    安全公告 Thinkphp5.0.*存在远程代码执行漏洞. 漏洞描述 Thinkphp5.0.*存在远程代码执行漏洞.攻击者可以利用漏洞实现任意代码执行等高危操作. 目前官方已经出了补丁: https ...

  4. JavaScript--常用对象的属性及方法(3)

    String对象(字符串) 字符串在本质上也是数组 都可以通过str[i]访问内容 但是数组创建后可以修改 而字符串一旦创建内容不可更改 属性:length 作用与数组相同 获取字符串的长度 方法: ...

  5. 前端知识总结--js异步事件顺序

    js中异步事件中容易混淆的 Promise 和 setTimeout 的执行顺序是怎样的? setTimeout(() => console.log(1), 0); new Promise(fu ...

  6. stm32 定时器 通用定时器

    STM32F10xxx 2个基本定时器(TIM6.TIM7) 4个通用定时器(TIM2. TIM3. TIM4和TIM5) 2个高级定时器(TIM1.TIM8) 每个定时器都是完全独立的,没有互相共享 ...

  7. 逐步探究ObjC的Weak技术底层

    前言 之前的文章有说过 Atomic 原子操作的原理,其作为一个特殊的修饰前缀,影响了存取操作. 在属性修饰定义中,还有另一类修饰前缀,他们分别是 strong weak assign copy,这些 ...

  8. git——日常保险操作

    git fetch --all //拉取远程最新代码 git reset --hard origin/master //如果本地无法更新,使用远程覆盖本地 git branch old_master ...

  9. HTML之表格标签和form表单

    表格标签: table 一般用于信息展示 tr行 td文本单元格 th标题单元格(文本加粗) table属性: cellspacing:单元格间距,一般设置为0 cellpadding:文字到边框的距 ...

  10. 6.Hbase 原理

    2 HBase体系结构 2.1 设计思路 HBase是一个分布式的数据库,使用Zookeeper管理集群,使用HDFS作为底层存储.在架构层面上由HMaster(Zookeeper选举产生的Leade ...