最小生成树:

  一个有 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. java毫秒级别定时器

    java每100毫秒执行一次 //每100毫秒秒执行一次 @Scheduled(fixedRate = 100) public void testScheduler() { System.out.pr ...

  2. K2 BPM_快消零售连锁行业门店选址解决方案_十年专注业务流程管理系统

    >>>业务流程管理软件选型攻略  快消零售连锁行业门店选址解决方案   业内有句名言:“门店最重要的是什么?第一是选址,第二是选址,第三还是选址” 选址是一个很复杂的综合性商业决策过 ...

  3. leetcode-29.两数相除(不用乘除法和mod)

    如题,不用乘除法和mod实现两数相除. 这里引用一位clever boy 的解法. class Solution { public: int divide(int dividend, int divi ...

  4. flex布局实战

    1.实现盒子的水平垂直居中 .parent{ width:200px; height:200px; display:flex; align-items: center; justify-content ...

  5. css多行超出时,超出高度,显示省略号

    .layout display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;

  6. CSS 样式表{二}

    1 选择器的优先级 选择器的优先主要考虑选择器的权重 可以将各种选择器的权重以数值来表示,数值越大,优先级越高 选择器 权重值 标签selector 1 类选择器 10 ID选择器 100 行内样式 ...

  7. ImportError: No module named yaml

    问题: import yaml ImportError: No module named yaml 解决: wget http://pyyaml.org/download/pyyaml/PyYAML- ...

  8. webpack与浏览器缓存

    根据之前的配置,假设文件上传至服务器中,没有加hash,如果页面内容有更改,浏览器刷新的时候,请求的还是原先的文件,也就是浏览器的缓存,因为名字没有变.现在我们在上线的webpack配置中加上hash ...

  9. vue 项目中使用postMessage问题总结

    问题描述: 由于目前做的项目分成两个项目,通过iframe嵌套,所以用到了 postMessage 当监听传过来的值的时候  出现了接受多次的问题 产生原因: 我的监听事件是放在home页 mount ...

  10. Java8-Lock-No.05

    import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util ...