[图] 最小生成树-Prime算法和Kruskal算法
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[图] 最小生成树-Prime算法和Kruskal算法相关的知识,希望对你有一定的参考价值。
参考技术A普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vojtěch Jarník)发现;并在1957年由美国计算机科学家罗伯特·普里姆(英语:Robert C. Prim)独立发现;1959年,艾兹格·迪科斯彻再次发现了该算法。因此,在某些场合,普里姆算法又被称为DJP算法、亚尔尼克算法或普里姆-亚尔尼克算法。
4 .输出:使用集合 V new 和 E new 来描述所得到的最小生成树。
下面对算法的图例描述
反证法:假设prim生成的不是最小生成树
这里记顶点数v,边数e
邻接矩阵:O(v 2 )
邻接表:O(e * log 2 v)
Kruskal算法是一种用来寻找最小生成树的算法,由Joseph Kruskal在1956年发表。用来解决同样问题的还有 Prime 算法和 Boruvka 算法等。三种算法都是贪婪算法的应用。和 Boruvka 算法不同的地方是,Kruskal 算法在图中存在相同权值的边时也有效。
图例描述:
对图的顶点数 n 做归纳,证明 Kruskal 算法对任意 n 阶图适用。
归纳基础:
n = 1,显然能够找到最小生成树。
归纳过程:
假设 Kruskal 算法对 n ≤ k 阶图适用,那么,在 k + 1 阶图 G 中,我们把最短边的两个端点 a 和 b 做一个合并操作,即把 u 与 v 合为一个点 v\',把原来接在 u 和 v 的边都接到 v\' 上去,这样就能够得到一个 k阶图 G\'(u ,v 的合并是 k + 1 少一条边),G\' 最小生成树 T\' 可以用Kruskal 算法得到。
我们证明 T\' + <u,v> 是 G 的最小生成树。
用反证法,如果 T\' + <u,v> 不是最小生成树,最小生成树是 T,即W(T) < W(T\' + <u,v>)。显然 T 应该包含 <u,v>,否则,可以用<u,v> 加入到 T 中,形成一个环,删除环上原有的任意一条边,形成一棵更小权值的生成树。而T - <u,v>,是 G\' 的生成树。所以 W(T-<u,v>) <= W(T\'),也就是 W(T) <= W(T\') + W(<u,v>) = W(T\'+<u,v>),产生了矛盾。于是假设不成立,T\' + <u,v>是 G 的最小生成树,Kruskal 算法对 k+1 阶图也适用。
由数学归纳法,Kruskal 算法得证。
e * log 2 e (e为图中的边数)
最小生成树-kruskal算法
连通图的一棵生成树是包含图的所有顶点的连通无环子图。
加权连通图的一棵最小生成树是图的一棵权重最小的生成树,其中,树的权重定义为所有边的权重总和。
最小生成树问题就是求一个给定的加权连通图的最小生成树问题。
最小生成树的算法主要有prim算法和kruskal算法,这篇主要讲解和实现后者。
kruskal算法主要是基于并查集+贪心的算法。该算法开始的时候,会按照权重的非递减顺序对所有的边进行排序,然后从一个空子图开始,扫描这个有序列表,并试图将列表中的下一条边加到当前的子图中。但是,如果加上这条边产生了回路,则把这条边跳过。
#include <iostream> #include <algorithm> using namespace std; const int N=150; int father[N],r[N],u[N],v[N],w[N];//定义father为每个节点的父亲,r为每条节点的编号,u,v为节点,w为边的权重 int find(int x)//并查集的核心算法,找寻父亲节点 { int r=x; while(father[r]!=r) { r=father[r]; } return r; } bool cmp(const int a,const int b)//主要用于sort函数中对于边的权重的排序 { return w[a]<w[b]; } int kruskal(int n,int m)//核心算法 { int mst=0,cnt=0; for(int i=0;i<n;i++)//初始化每个节点的父亲为自己本身 { father[i]=i; } for(int i=0;i<m;i++)//记录每条边的序号 { r[i]=i; } sort(r,r+m,cmp);//将边按照权重从小到大排序,这里只改变序号,就可以知道是哪条边 for(int i=0;i<m;i++)//对每条边来说,都判断边的两个顶点是否连通,如果连通,则证明加入之后,会出现回路,不予考虑 { int e=r[i]; int x=find(u[e]); int y=find(v[e]); if(x!=y) { mst+=w[e]; father[x]=y; cnt++; } } if(cnt<n-1)//证明此图不连通,不存在最小生成树 mst=0; return mst; } int main() { int n,m; cin>>n>>m; for(int i=0;i<m;i++) { cin>>u[i]>>v[i]>>w[i]; } int mst=kruskal(n,m); if(mst==0) cout<<"the mst is not found"<<endl; else cout<<"the mst is "<<mst<<endl; return 0; }
以上是关于[图] 最小生成树-Prime算法和Kruskal算法的主要内容,如果未能解决你的问题,请参考以下文章