图论——最小生成树:Prim算法及优化、Kruskal算法,及时间复杂度比较
最小生成树:
一个有 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 条边为止。
步骤:
- 新建图G,G中拥有原图中相同的节点,但没有边;
- 将原图中所有的边按权值从小到大排序;
- 从权值最小的边开始,如果这条边连接的两个节点于图G中不在同一个连通分量中,则添加这条边到图G中;
- 重复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算法,及时间复杂度比较的更多相关文章
- 【数据结构】 最小生成树(四)——利用kruskal算法搞定例题×3+变形+一道大水题
在这一专辑(最小生成树)中的上一期讲到了prim算法,但是prim算法比较难懂,为了避免看不懂,就先用kruskal算法写题吧,下面将会将三道例题,加一道变形,以及一道大水题,水到不用高级数据结构,建 ...
- 算法起步之Kruskal算法
原文:算法起步之Kruskal算法 说完并查集我们接着再来看这个算法,趁热打铁嘛.什么是最小生成树呢,很形象的一个形容就是铺自来水管道,一个村庄有很多的农舍,其实这个村庄我们可以看成一个图,而农舍就是 ...
- 算法8-4:Kruskal算法
Kruskal算法用于计算一个图的最小生成树.这个算法的过程例如以下: 依照边的权重从小到达进行排序 依次将每条边添加到最小生成树中,除非这条边会造成回路 实现思路 第一个步骤须要对边进行排序,排序方 ...
- 算法笔记_066:Kruskal算法详解(Java)
目录 1 问题描述 2 解决方案 2.1 构造最小生成树示例 2.2 伪码及时间效率分析 2.3 具体编码(最佳时间效率) 1 问题描述 何为Kruskal算法? 该算法功能:求取加权连通图的最小 ...
- 图论--最小生成树--Prim算法(带边输出)模板
#include <bits/stdc++.h> using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 100 ...
- 并查集及其简单应用:优化kruskal算法
并查集是一种可以在较短的时间内进行集合的查找与合并的树形数据结构 每次合并只需将两棵树的根合并即可 通过路径压缩减小每颗树的深度可以使查找祖先的速度加快不少 代码如下: int getfather(i ...
- 图论——最小生成树prim+邻接表+堆优化
今天学长对比了最小生成树最快速的求法不管是稠密图还是稀疏图,prim+邻接表+堆优化都能得到一个很不错的速度,所以参考学长的代码打出了下列代码,make_pair还不是很会,大体理解的意思是可以同时绑 ...
- 最小生成树 (Minimum Spanning Tree,MST) --- Kruskal算法
本文链接:http://www.cnblogs.com/Ash-ly/p/5409265.html 引导问题: 假设要在N个城市之间建立通信联络网,则连通N个城市只需要N - 1条线路.这时,自然会考 ...
- Electrification Plan 最小生成树(prim+krusl+堆优化prim)
题目 题意: 无向图,给n个城市,n*n条边,每条边都有一个权值 代表修路的代价,其中有k个点有发电站,给出这k个点的编号,要每一个城市都连到发电站,问最小的修路代价. 思路: prim:把发电站之间 ...
随机推荐
- [JZOJ5897]密匙--哈希骚操作
[JZOJ5897]密匙--哈希骚操作 题目链接 太懒了自行Google 前置技能 二分/倍增求LCP e.g TJOI2017DNA 分析 这题看了样例解释才知道什么意思 本以为自己身为mo法师蛤希 ...
- Linux 命令行:cURL 的十种常见用法
Linux 命令行:cURL 的十种常见用法 文章目录 1. 获取页面内容 2. 显示 HTTP 头 3. 将链接保存到文件 4. 同时下载多个文件 5. 使用 -L 跟随链接重定向 6. 使用 -A ...
- IDEA安装及默认配置习惯配置(二)
安装完后,接下来配置Idea使用习惯. 一.基本使用 1.字体设置 2.修改编码模式 3.显示行号和方法分割线 4. 格式化代码时候多行空行合并为1行 5.代码提示不区分大小写 6.自动导包设置 7. ...
- 用jq动态给导航菜单添加active
点击后页面跳转到了新的链接,找到所有的li下的a标签,对其链接地址进行判断,如果和当前浏览器的地址一致,就认为是当前应该激活的菜单,添加active类,否则就取消. <ul class=&quo ...
- Array + two points leetcode.18 - 4Sum
题面 Given an array nums of n integers and an integer target, are there elements a, b, c, and d in num ...
- Dephi 10.3.3试用报告
官方没有正式发布,但出了下载及keygen,具体内容我在这篇内容写了:Delphi 10.3.3最新消息 . 也可以去Delphi多层开发交流QQ群:209321818找相关的keygen. 今早来就 ...
- 第五章、drf-JWT认证
目录 JWT认证 JWT认证方式与其他认证方式对比: 优点 格式 drf - jwt 插件 官网 安装 token的签发与校验初识: 签发token 校验token 自定义drf-jwt配置 sett ...
- 爬虫的新模块pyppeteer的使用
安装 python3 -m pip install pyppeteer 最好是py3.5+ 手动安装 你懂的,天朝网络环境很复杂,如果要用pyppeteer自己绑定的chromium,半天都下载不下来 ...
- 创建守护进程步骤与setsid()
原创:http://www.cnblogs.com/mickole/p/3188321.html 一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且 ...
- 跨域详解之jsonp,底层的实现原理
分享一下跨域,不仅是因为现在的工作中遇到的越来越多,而且在面试中也经常被问到. 那么什么是跨域呢,我们来看官方给出的解释:浏览器不能执行其他网站的脚本.它是由浏览器的同源策略造成的(所谓同源是指,域名 ...