• 图论六:最小生成树


    问题描述:在无向图中找出一个最小生成树,前提是图是连通的。

    一、Prim算法

    1、思路:贪心,每次更新每个节点的距离,使这个节点的距离最短,类似于dijkstra算法

    2、使用条件:无向连通图

    3、算法实现:

    (1)找到一个起始点,(终点有没有都无所谓),将起点的距离设为0(表示起点到起点的距离是0),

    标记每个节点的数组vis初始化为0,pre数组记录最小生成树的边。

    (2)更新每个点的最短距离,每次一个节点,与dijkstra不同的是,在更新边的时候,dij是更新起点到这一点的

    最短距离,而Prim只更新这一点和它的邻接点之间的距离。

    (3)重复(2)操作,直到所有点都被标记了。

    4、代码:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int maxn = 1200;
    const int INF = 0x3fff;
    int edge[maxn][maxn],pre[maxn],vis[maxn],dis[maxn],m,n,st,ed;
    void Init()
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            edge[i][j]=(i==j?0:INF);
        for(int i=1;i<=n;i++) pre[i]=0,vis[i]=0,dis[i]=INF;
        dis[st]=0;
    } 
    void Prim()
    {
        while(1)
        {
            int mi=INF,pos=-1;
            for(int i=1;i<=n;i++)
            if(vis[i]==0&&dis[i]<mi)
            {
                mi=dis[i];pos=i;
            }
            
            if(pos==-1) return ;
            vis[pos]=1;
            for(int i=1;i<=n;i++)
            if(vis[i]==0&&edge[pos][i]<dis[i]&&edge[pos][i]!=INF)
            {
                dis[i]=edge[pos][i];pre[i]=pos;
            }
        }
    }
    int main(void)
    {
        int x,y,z;
        cin>>n>>m;
        st=1;ed=n;
        Init();
        for(int i=1;i<=m;i++)
        {
            cin>>x>>y>>z;
            edge[x][y]=edge[y][x]=z;
        }
        Prim();
        int sum=0;
        for(int i=2;i<=n;i++)
        cout<<"("<<pre[i]<<","<<i<<")"<<endl,sum+=dis[i];
        cout<<sum<<endl;
        return 0;
    }
    /*
    6 7
    1 2 1
    2 3 7
    3 4 6
    4 5 5
    5 6 2
    1 6 3
    2 5 4
    */
    View Code

    5、算法复杂度:O(|V|^2)

    二、Kruskal算法

    1、思路:并查集&优先队列

    2、适用条件:无相连通图

    3、算法实现:

    (1)建立一个边的集合,建立n个点的数组,将它们初始化为1-n,标记数组初始化为0

    (2)输入m条边的信息,按照边的权值大小排序,然后从权值小的边开始合并点集,直到所有点都是一个集合为止。

    (3)遍历vis数组,输出合并的边和最终的最小生成树的大小。

    4、代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn = 1200;
    const int INF = 0x3ffff;
    struct Edge{
        int u,v,w;
    }edge[maxn];
    int n,m,a[maxn],vis[maxn];
    bool cmp(Edge A,Edge B)
    {
        return A.w<B.w;
    }
    void Init()
    {
        for(int i=1;i<=n;i++) a[i]=i;
        memset(edge,0,sizeof(edge));
        memset(vis,0,sizeof(vis));
    }
    int f(int x)
    {
        if(x==a[x]) return a[x];
        else return f(a[x]);
    }
    void Kruskal()
    {
        int sum=0;
        for(int i=1;i<=m;i++)
        {
            int t1=f(edge[i].u),t2=f(edge[i].v);
            if(a[t2]!=t1)
            {
                a[t2]=t1;
                vis[i]=1;
                sum+=edge[i].w;
            }
        }
        for(int i=1;i<=m;i++)
        if(vis[i]==1) printf("(%d,%d)
    ",edge[i].u,edge[i].v);
        printf("%d
    ",sum);
    }
    int main(void)
    {
        int i,x,y,z;
        Edge tmp;
        cin>>n>>m;
        Init();
        for(i=1;i<=m;i++)
        {
            cin>>x>>y>>z;
            tmp.u=x;tmp.v=y;tmp.w=z;
            edge[i]=tmp;
        }
        sort(edge+1,edge+m+1,cmp);
        Kruskal();
        return 0;
    }
    View Code

    5、复杂度:O(|E|log|V|)。

    总结:两种最小生成树的算法都是找最小的边,但是Prim算法是由点找边,Kruskal算法是直接找边,所以Prim算法可以视为“点插法”,

    Kruskal算法可以视为“边插法”。

  • 相关阅读:
    指针详解(C语言)
    JMeter接口测试示例(六)——上传文件
    JMeter接口测试示例(五)——添加Cookie
    JMeter接口测试示例(四)——添加HTTP信息头
    JMeter接口测试参数化(函数生成器)
    JMeter接口测试——参数化
    Bootstrap简介
    jQuery文档节点处理,克隆,each循环,动画效果,插件
    什么是 jQuery 和jQuery的基本选择器,层级选择器,基本筛选器
    BOM对象,math对象document对象的属性和操作和 事件的基本操作
  • 原文地址:https://www.cnblogs.com/2018zxy/p/10142354.html
Copyright © 2020-2023  润新知