[图] 最小生成树-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算法的主要内容,如果未能解决你的问题,请参考以下文章

最小生成树 prime kruskal

最小生成树

最小生成树(MST) prime() 算法 kruskal()算法 A - 还是畅通工程

最小生成树-kruskal算法

急求KRUSKAL算法求最小生成树过程演示

图论学习