最小生成树MST-克鲁斯卡尔(Kruskal)算法

(Minimum Spanning Tree)

http://blog.csdn.NET/dellaserss/article/details/7724401/

https://segmentfault.com/a/1190000004023326

http://blog.csdn.Net/w1085541827/article/details/52076481

先抛出个问题,什么是并查集,它有什么用?

其实并查集顾名思义就是有“合并集合”和“查找集合”两种操作的关于数据结构的一种算法

1 什么是并查集,以及并查集要完成的目标。

举个例子,通火车要修路,已经修了一部分了,但各个地方零零散散的没有统一成一个整体的铁路网,中国这么多地方,我不能每个地方都直接联系,这样花费的代价也太大了。所以我们这样想,只要从一个地方能去中国任意一个地方就好了,用不着每个地方都相互有专门的铁路。这就衍生了一个问题,我怎么知道我要去的地方在不在我这个铁路网里?我该不该修到这儿的铁路?不好判断是吧,这就用到了并查集思想。

并查集主要用在判断一个图中的两个顶点是否能相联通的问题

2 实现并查集的思想。

既然顶点与顶点能直接相同,那么他们一定处于同一张连通网中。因此,我们可以把不同的连通网分别看成一个个集合。我们要判断两点是否相通,可以检索这两点是否在同一张连通网里,在就能相通,反之不能。

所以,我们应该怎样设计,使得我们能判断两点是否在同一张网里是问题的关键。

这时,我们自然而然的想到了,如果每一个点都能用这个网中的固定点表示(姑且把这个点称为BOSS),那么问题就解决了。例如下图,我们要判断A和B是否能相通,A找啊找

找到A的BOSS是BOSS1,B也找啊找,找到B的BOSS也是BOSS1。说明他们位于同一张联通图中,即他们能过去。


   但是这找BOSS还是好麻烦,不好找,如果是两棵树的话就好多了,直接以根节点作为所有节点的BOSS,每棵树只需要知道它自己的上级是谁,一层一层往上找,最终就找到BOSS了。例如:要找G和B是否位于同一张网,G开始找BOSS,G的上级是D,D的上级是A,A就是BOSS,返回G的BOSS为A。同理,B找到BOSS也是A。说明他们位于同一张网中。再例如我要判断E和N是否能相通,我就找E的BOSS,再找N的BOSS,明显A不等于H。所以他们不能连通。


总而言之,并查集就是把每堆元素合并为一个具有相同BOSS的集合,如果两堆元素BOSS不同,说明他们不连通,反之连通。

3 实例代码实现

我认为主体分为四个部分

(1)建立并查集(用数组也好,链表也行),我就用数组举个例子

            int parent[maxSize];

           i表示顶点编号 parent[i]表示i对应的上级 !!!

           这里下标代表顶点编号,元素代表它的上级(就是通过上级找上级.....最终能找到它的BOSS)。

(2)初始化并查集

for(int i  = 0 ; i < N;++i)   
{  
   parent[i] = i;  
} 

 这里的N代表顶点的个数,首先默认每个顶点都不能互相联系,每一个顶点自己就是一个集合,故a[i] = i;它的上级就是它自己,它就是BOSS。

(3)构造一个查找BOSS的算法,我用迭代的方式给出(递归也能写)

int getBoss(int a)  
{  
    while(a != parent[a])    
    {  
        a = parent[a];  
    }  
    return a ;  
}  

while循环的意思的如果传进来的a已经是BOSS,直接输出,否则进行查找([注]根据存放的值可以找到它上级,它的上级又继续查找它上级的上级,直到a==parent[a]说明找到了,

(4)判断将顶点合并进同一个集合(有同一个BOSS),我用迭代的方法做个例子

void merge()  
{  
    int a , b;  
    a = getBoss(c);  
    b = getBoss(d);  
    if(a != b)  
    {  
    parent[a] = b;  
    }  
}  

      c,d代表传进来的顶点元素下标,如果两个BOSS不相等,说明不在同一棵树中,即不会产生循环,把b归于a的集合,从此他们就在一棵树中,共同拥有一个BOSS

最后举出一些实际应用的例子

整幅图的连通性问题。比如随意给你两个点,让你判断它们是否连通,或者问你整幅图一共有几个连通分支,也就是被分成了几个互相独立的块。问还需要修几条路,实质就是求有几个连通分支。等等。。

typedef struct//建立边的集合  
{  
    int begin;//一个边的开始  
    int end;//一个边的结束  
    int weight;//这个边的权值  
}Edge;  
  
int Find(int* parent, int f)//查找跟节点的函数  
{  
    while(parent[f] != f)//如果f传过来的上级不是parent[f]  
    //要根据f找它上级。它的上级继续找它的上级;知道parent[f] == f 就输出  
    {  
        f = parent[f];  
    }  
    return f;  
}  
  
void Kruskal(MGraph G)  
{  
    Edge edges[numE];//定义边集数组 用来存放所有边  
    int parent[numV];//定义一个数组来判断是否与边形成环  
    //此行 将邻接矩阵 转化为边集数组edges 并按照由小到大排序  
    for(int i=0;i<G.numV; i++)//顶点  
    {  
        parent[i] = i;//初始化每个顶点的上级是自身  
    }  
    for(int i=0;i<G.numE;i++)//循环每一条边  
    {  
        int n = Find(parent, edge[i].begin);  
        int m = Find(parent, edge[i].end);  
        if(n!= m)//说明m和n 在不同的联通集里面   
        {  
            parent[n] = m;//将m跟n 的两个联通集 通过(n.m)连接起来  
            cout<<"("<<edges[i].begin<<","<<edges[i].end  
            <<") "<<edges[i].weight;  
        }  
    }  
} 

 

     路径压缩算法:建立门派的过程是用join函数两个人两个人地连接起来的,谁当谁的手下完全随机。最后的树状结构会变成什么胎唇样,我也完全无法预计,一字长蛇阵也有可能。这样查找的效率就会比较低下。最理想的情况就是所有人的直接上级都是掌门,一共就两级结构,只要找一次就找到掌门了。哪怕不能完全做到,也最好尽量接近。这样就产生了路径压缩算法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SoWhat1412

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值