最小生成树 笔记
一、最小生成树是什么
-
生成树,由一个连通图的所有顶点和其中若干条边构成的树。
-
最小生成树,指一个连通图的所有生成树,边权之和最小的一个。
二、实现方法
1. Prim
(1). 流程
Prim 算法的流程如下:
-
初始时从图中取一节点加入树。
-
选择一个与当前树中顶点距离最近的顶点,将该顶点和相应的边加入树。
-
更新每个顶点离该顶点的距离。
-
当所有的顶点都在树中时,这棵树就是最小生成树。
(2). 时间复杂度
Prim 和 Dijkstra 有异曲同工之妙,时间复杂度的瓶颈在第二步:选距离最近的顶点。直接使用循环枚举这一步时,时间复杂度为 $O(n^2)$,但是,可以用优先队列来优化这一部分的耗时。因此,Prim 最终的时间复杂度是 $O(n\log n)$。
(3). 代码实现
#include<bits/stdc++.h> #define int long long #define pii pair <int,int> using namespace std; void fread(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); } const int N=2e5+10,inf=0x7fffffff; int n,m,head[N],idx,dis[N],cnt,ans; bool vis[N]; struct EDGE{ int v,w,next; }edge[N*2]; void add(int u,int v,int w){ edge[++idx]=(EDGE){v,w,head[u]}; head[u]=idx; } void prim(){ fill(dis+2,dis+N,inf); priority_queue<pii,vector<pii>,greater<pii>>q; q.push({0,1}); while(cnt<n and !q.empty()){ pii t=q.top(); q.pop(); int dist=t.first; int u=t.second; if(vis[u])continue; vis[u]=true; cnt++; ans+=dist; for(int i=head[u];i;i=edge[i].next){ int v=edge[i].v,w=edge[i].w; if(!vis[v] and w<dis[v]){ dis[v]=w; q.push({w,v}); } } } } signed main(){ fread(); scanf("%lld%lld",&n,&m); for(int i=1;i<=m;i++){ int u,v,w; scanf("%lld%lld%lld",&u,&v,&w); add(u,v,w); add(v,u,w); } prim(); if(cnt!=n)cout<<"impossible"; else cout<<ans; return 0; }
2. Kruskal
(1). 流程
Kruskal 极为通俗易懂,算法流程如下:
-
将所有边按边权从小到大排序,由于要存储边的起点和终点,因此不再选用链式前向星,而是用与 Bellman-ford 相同的存图方式。
-
将边从小到大选,如果选择了当前这一条边之后,不会成为一张图(不会出现环),就将这一条边添加至树中。可以选用并查集来判断是否有环。
-
当选够 $n-1$ 条边时,树就构成了,此时得到的树就是最小生成树。
(2). 时间复杂度
Kruskal 的时间复杂度的瓶颈在第一步排序,因此 Kruskal 的时间复杂度是 $O(m\log m)$
关于 Prim 和 Kruskal 选用谁更好,那就要比较 $n$ 与 $m$ 谁大,边比点多时用 Prim,点比边多时用 Kruskal。
(3). 代码实现
#include<bits/stdc++.h> #define int long long #define pii pair <int,int> using namespace std; void fread(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); } const int N=2e5+10,inf=0x7fffffff; int n,m,dis[N],cnt,ans,f[N]; bool vis[N]; struct EDGE{ int u,v,w; }edge[N*2]; bool cmp(EDGE x,EDGE y){ return x.w<y.w; } int gf(int x){ if(f[x]!=x)f[x]=gf(f[x]); return f[x]; } void kruskal(){ for(int i=1;i<=n;i++){ f[i]=i; } for(int i=1;i<=m;i++){ int u=edge[i].u; int v=edge[i].v; int w=edge[i].w; int fu=gf(u); int fv=gf(v); if(fu!=fv){ f[fu]=fv; ans+=w; cnt++; } } } signed main(){ fread(); scanf("%lld%lld",&n,&m); for(int i=1;i<=m;i++){ int u,v,w; scanf("%lld%lld%lld",&u,&v,&w); edge[i]=(EDGE){u,v,w}; } sort(edge+1,edge+m+1,cmp); kruskal(); if(cnt!=n-1)cout<<"impossible"; else cout<<ans; return 0; }
本文作者:Garbage fish's Blog
本文链接:https://www.cnblogs.com/Garbage-fish/p/17825001.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步