Kruskal算法


  Kruskal算法和Prim算法都是用于查找无向带权图中的最小生成树。但是Kruskal算法的时间复杂度为O(Elog2(E)),而Prim算法则是O(Elog2(n)),考虑到最复杂的情况|E|=|V|^2,这时候Kruskal算法的上限为O(2Elog2(V)),与Prim算法相差不大。并且Kruskal的核心算法为排序和并查集,相较于Prim算法需要实现最小堆和迪杰卡斯特算法,显然要简单很多。

  对于图G(V,E)中的最小生成树U,我们可以证明E中权重最小的边一定属于U。假设(u,v)为权重最小的边,且(u,v)不属于U,则在U中存在一条从u到v的路径u,x,...,v,显然我们可以通过从U中移除(u,x)并加入(u,v)降低U的权重,这与U是最小生成树的前提相悖,故命题成立。

  在完成了上面一步之后,我们可以将u和v合并做一个结点,从而得到新的顶点集合V',同时从E中移除(u,v)得到新的边集E',我们可以保证G(V',E')中的最小生成树T满足 T=U\{(u,v)}。可以反向说明,假设我们找到了G(V',E')的最小生成树T,此时我们将某个结点分裂作u与v,并用(u,v)连通u和v,并要求新图中的最小生成树必须包含(u,v)。假设我们此时得到了新的最小生成树PU{(u,v)},然而我们重新合并u与v之后,P将成为G(V',E')的一个生成树,但是它不是最小的,故TU{(u,v)}的权重必定小于PU{(u,v)},因此T=U\{(u,v)}。利用递归的思路可以通过不断查找最小边来找到集合中的最小生成树。

  上面的过程中我们可以通过并查集来实现顶点的合并,并且利用任意O(nlog2(n))时间复杂度的排序算法对所有边进行预先排序,之后按权重从小到大遍历所有边,如果边的两端未被合并入一个顶点(不在同一个并查集中),那么就将边两端的顶点合并,并将边加入最小生成树中。等到整个流程结束就可以得到最小生成树,显然合并最多发生|V|-1次,因此最小生成树中包含|V|-1条边。且由于我们取得的边是优先取未被移除的低权边,因此得到的每条边都是正确的。时间复杂度决定于对边的排序这一流程,为O(Elog2E)。

 1 kruskal()
 2     edgeList = empty-list
 3     mst = empty-list
 4     for edge in E
 5         edgeList.add(edge)
 6     sort(edgeList)
 7     for edge in edgeList
 8         if(edge.src.findSet() != edge.dst.findSet())
 9             union(edge.src, edge.dst)
10             mst.add(edge)
11     return mst

 

 

 

  


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM