最小生成树-kruskal算法

算法思想:

设G=(V,E)是带权连通图,V={v1,v2,v3......vn}

首先将G的n个顶点看成n个孤立的连通分量,将所有的边从小到大排序,从第一条边开始查看每一个边,只要不连成回路即可

具体实现:

克鲁斯卡尔算法执行的过程中,构造的生成树是由若干个自由树组成的森林,算法在选择一条最小权值的边(u,v)时,必须确信边(u,v)加入后不会形成回路,
这就要求u和v是分别属于生成森林的两棵不同的树上的,否则就选择下一条最小边。通过并查集来实现。
并查集的每个子集合代表一棵自由树,组成的森林就是并查集。事实上,并查集就是用森林表示的,可以利用并查集类的Find成员函数判断一条边
(u,v)的两个顶点是否在同一个子集合中,即是否在一棵树上,若不在同一子集合中,则可以将边(u,v)加入T,否则舍弃之。在将边(u,v)
加入T之后,应将这两个顶点所在的子集合使用union函数合并成一个子集合。


并查集:一种树形的数据结构,用于处理一些不相交的集合的合并和查询,常常在使用中以森林来表示。
(1)查找一个元素所在的集合
查找一个元素所在的集合,只要找到这个元素所在集合的祖先。先设置一个数组Father[x]表示x的父亲的编号,即可以转为寻找
两个元素的最久远的祖先是否相同,可以采用递归实现。
(2)合并两个不相交的集合
找到其中一个集合最久远的祖先,将另外一个集合最久远的祖先指向他就可以



    #include<iostream>  
    #include<string.h>
    #include<algorithm>  
    using namespace std;  
      
    #define N 1000  
    struct graph  
    {  
        int u,v,cost;  //顶点u和顶点V的边权是cost
        void set(int a,int b,int w){u=a,v=b,cost=w;}  
    };  
      
    graph d[N*(N+1)/2];  //对于有n个顶点的图,边的数量是e<=n(n+1)/2
      
    int father[N];  
    int Find(int x)  
    {  
        if (father[x]==-1) return x;  
        return father[x] = Find(father[x]);  
    }  
      
    bool Union(int x, int y)  
    {  //判断两元素是否属于同一个集合,只要看看他们所在的集合的祖先是否相同
        x = Find(x);  
        y = Find(y);  
        //祖先相同,是一个集合
        if (x==y) return false;  
        //祖先不同,合并两个集合
        if (x>y) father[x] = y;  
        if (x<y) father[y] = x;  
        return true;  
    }  
     //按照边权的递增顺序排序
    int cmp(graph x,graph y)  
    {  
        if (x.cost<y.cost) return true;  
        return false;  
    }  
      
    int main()  
    {  
        int n, m;  
        while (scanf("%d%d", &n, &m)!=EOF && n)  
        {  
            int i;  
            int v,w,edge;
            //假设有m条边,每条边描述是(v,w,edge)表示顶点v到顶点w的边权是edge,则构造边集合d如下:
            for (i=0; i<m; i++)  
            {  
                scanf("%d%d%d", &v, &w, &edge);  
                d[i].set(v,w,edge);  
            }  
            sort(d, d+m, cmp);  
            memset(father, -1, sizeof(father));  
            int sum=0;  //最小生成树中边权的和
            int count=0;  
            //查找每一条边
            for(i=0; i<n*(n+1)/2; i++)  
            {  
            //找到一条满足的边
                if (Union(d[i].u, d[i].v))  
                {  
                    printf("%d %d\n", d[i].u, d[i].v);  
                    sum += d[i].cost;  
                    ++count;  
                }  
                if (count==n-1) break;  //成功将所有的顶点连通
            }  
            printf("%d\n",sum);  
        }  
    } 




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值