图论基础最小生成树之kruskal算法并查集版(加边法)

#include<stdio.h>
#include<stdlib.h>
typedef struct
{
   int x,y;
   int w;    
}edger;

edger edge[900];
int father[900],dian,bian,cost;
*father数组记录集合的代表人物,每次代表人物选取该集合中的最小数字作为代表,这是并查集的典型运用*
void paixu()
{
    int i,j,k;
     for(i=bian;i>1;i--)
      for(j=0;j<i;j++)
         if(edge[j+1].w<edge[j].w)
            {k=edge[j].x;
             edge[j].x=edge[j+1].x;
             edge[j+1].x=k;
            
             k=edge[j].y;
             edge[j].y=edge[j+1].y;
             edge[j+1].y=k;
            
             k=edge[j].w;
             edge[j].w=edge[j+1].w;
             edge[j+1].w=k;
           }
}
*将各个边的权值进行冒泡排序,从而方便进行后面的贪心策略*
void setfather(int i)
{
     int r,l;
     l=findfather(edge[i].x);
     r=findfather(edge[i].y);
     if (r<l)
     father[l]=r;
     else
     father[r]=l;
}
*对不在一个集合内的两点进行处理(也就是说这条边还没有加入到这棵最小生成树里面去),对他们的代表人物进行重新整合建立,先找出它们各自的代表人物,然后比较两个代表人物的大小,小的数从而成为新的代表人物(也就是说大一点的代表人物的代表人物从自己变成了更小的另一个)*

int findfather(int k)
{
    while(k!=father[k])
      k=father[k];   
    return k;      
}
*找到这个数的祖宗的祖宗的祖宗……就这样一直找下去,直到找到一个祖宗是自己的数为止,那么这个数显然是这一个集合的代表人物*

int main()

    int i,j,k;
    scanf("%d%d",&dian,&bian);
    
    for(k=1;k<=dian;k++)
         father[k]=k;
*初始化祖宗,各自的祖宗最开始都是自己,也就是说自己是一个集合*
    for(k=1;k<=bian;k++)  
         scanf("%d%d%d",&edge[k].x,&edge[k].y,&edge[k].w);
*对边的数据结构进行扫描,这包括边的首末两点以及这条边的权值* 
    paixu();
*排序,贪心策略的前提*
    cost=edge[1].w;
    setfather(1);
*贪心策略的初始进行,首先采纳最小的那么一棵"树叶"从而而变成了树的"根",后面的再依次贪心加入*      
    for(i=2;i<=bian;i++)
          if(findfather(edge[i].x)!=findfather(edge[i].y))
             {
               cost=cost+edge[i].w;
               setfather(i);
             }
*则每采取贪心策略赚取一片树叶,那么这条边与之相关联的两点的祖宗就要重新设置一下,setfather,那么到最后一共有n-1条边*

printf("%d",cost);
     system("pause");
}

测试数据:

7 9
1 2 28
1 6 10
2 3 16
2 7 14
3 4 12
4 5 22
4 7 18
5 6 25
5 7 24

输出:

最小生成树各边:(1,6) (6,5) (5,4) (4,3) (3,2) (2,7)
最小代价:99

 

 

 

    prim和kruskal同为求最小生成树的算法,prim是加点法,而kruskal是加边法。

    异曲同工的是其核心思想是纯正的贪心策略。prim算法中,每次加入与树相关联最短的一个点,在把点加入该树的同时,利用该点进行树和其他点相关联权值的更新,然后再进行加点处理,一个一个加,直至把所有点都加满为止。在kruskal算法中,首先是将各条边进行按权值从小到大的排序,然后一个一个处理,加入适合的边,直到加到有n-1条边为止(或者说所有点都入账为止)。需要注意的是kruskal算法还运用的并查集的思想,每有一条边入账,那么这条边的两个顶点就要进行一次“祖宗点”的处理,使得两点同时进入最小生成树的集合内。

    如此一来,两种方法都很好的处理了最小生成树的问题。

个人觉得kruskal算法理解起来其实更为容易,但是在编译程序过程中还是有些小细节要注意的,特别是并查集的运用。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值