最小生成树——克鲁斯卡尔(Kruskal)算法详解和实现

克鲁斯卡尔算法是一种构造最小生成树的方法,主要步骤包括使用最小堆和并查集。在初始阶段,每个顶点独立成一个等价类,然后逐步选取最小权值边,通过并查集判断是否形成环,若不在同一连通分量则合并。算法效率为O(elog2e),适用于稀疏图。此外,讨论了遍历排序边和使用搜索判断连通性的替代方法。
摘要由CSDN通过智能技术生成

克鲁斯卡尔(Kruskal)算法

  1. 克鲁斯卡尔算法的基本思想是:设一个有n个顶点的连通网络G={V,E},
    (1)先构造一个包括全部n个顶点和0条边的森林F={T0,T1,…,Tn-1},
    (2)以后每一步向下中加入一条边(v,u),它应是所依附的两个顶点v和u分别在森林F的两棵不同的树上的所有边中具有最小权值的边。由于这条边的加入,使F中的某两棵树合并为一棵,树的棵数减一。
    (3)如此,经过n-1步,最终得到一棵有n-1条边且各边权值总和达到最小的生成树,即最小生成树。
  2. 例如,对于图7-18(a)所示的连通网络,图7-19中(a)~(f)给出了按克鲁斯卡尔算法生成最小生成树的过程。
    在这里插入图片描述
  3. 在克鲁斯卡尔算法中,利用最小堆来存放连通网络中的边,堆中每个元素代表连通网络中的一条边,它由三个域组成:adjvex1、adjvex2和 weight。
    其中adjvex1和adjvex2存储该边所依附的两个顶点的序号,weight存储边上的权值。
    利用并查集存放所有连通分量,同一个连通分量的顶点组成并查集的一个子集(等价类)
  4. 克鲁斯卡尔算法步骤如下。
    1)初始化,在并查集中,连通网络的每一个顶点独立成一个等价类,连通网络的所有的边建立最小堆,最小生成树T中没有任何边,T中边的条数计数器i为0。
    2)如果T中边的条数计数器i等于顶点数减1,则算法结束;否则继续步骤3)。
    3)选取堆顶元素代表的边(v,u),同时调整堆。
    4)利用并查集的运算检查依附于边(v,u)的两个顶点v和u是否在同一个连通分量(即并查集的同一个子集合)上,如果是则转步骤2);否则继续步骤5)。
    5)将边(v,u)加入到最小生成树T中,同时将这两个顶点所在的连通分量合并成一个连通分量(即并查集中的相应两个子集合并成一个子集),继续步骤2)。

代码实现

Kruskal算法代码

template<class ElemType,class WeightType> void MiniSpanTreeKruskal(const AdjMatrixUndirGraph<ElemType,WeightType> &g)
{
   
    int count,VexNum=g.GetVexNum();
    KruskalEdge<ElemType,WeightType> KEdge;
    MinHeap<KruskalEdge<ElemType,WeightType> > ha(g.GetArcNum());
    ElemType *kVex,v1,v2;
    kVex=new ElemType[VexNum];
    for(int i=0; i<VexNum; i++)
        g.GetElem(i,kVex[i]);
    UFSets<ElemType> f(kVex,VexNum);//根据顶点数组构造并查集
    for(int v=0; v<VexNum; v++)
        for(int u=g.FirstAdjVex(v); u>=0; u=g.NextAdjVex(v,u))
            if(v<u)//将v<u的边插入最小堆
            {
   
                g.GetElem(v,v1);
                g.GetElem(u,v2);
                KEdge.vertex1=v1;
                KEdge.vertex2=v2;
                KEdge.weight=g.GetWeight(v,u);
                ha.Insert(KEdge);
            }

    ///至此是初始化,将边加入最小堆中
    count=0;
    cout<<"Kruskal最小生成树需要连接一下边:"<<endl;
    while(count<VexNum-1)
    {
   
        ha.DeleteTop(KEdge);
        v1=KEdge.vertex1;
        v2=KEdge.vertex2;
        if(f.Differ(v1,v2))//边所依附的两顶点不在同一棵树上
        {
   
            cout<<"edge:( "<<v1<<" , "<<v2<<" )     weight:"<<KEdge.weight<<endl;
            f.Union(v1,v2);
            count++;
        }
    }
}

辅助代码

边类
template<class ElemType,class WeightType> class KruskalEdge
{
   
public:
    ElemType vertex1,vertex2;
    WeightType weight;
    KruskalEdge(ElemType v1,ElemType v2,WeightType w);
    KruskalEdge() {
   }
    KruskalEdge<ElemType,WeightType>& operator=(const KruskalEdge<ElemType,WeightType> &Ed);
    bool operator<=(const KruskalEdge<ElemType,WeightType> &Ed);
    bool operator>(const KruskalEdge<ElemType,WeightType> &Ed);
    friend ostream& operator<<(ostream &out,const KruskalEdge<ElemType,WeightType> &Ed)
    {
   
        out<<"( "<<Ed.vertex1<<" , "<<Ed.vertex2<<" ) weight:"<<Ed.weight;
        return out;
    }
};

template<class ElemType,class WeightType> KruskalEdge<ElemType,WeightType>::
    KruskalEdge(ElemType v1,ElemType v2,WeightType w)
{
   
    vertex1=v1;
    vertex2=v2;
    weight=w;
}
template<class 
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值