克鲁斯卡尔算法(Kruskal)

57 篇文章 1 订阅
52 篇文章 2 订阅

  克鲁斯卡尔(Kruskal)算法是一种按权值的递增次序选择合适的边来构造最小生成树的方法。假设 G=(V,E) 是一个具有 n 个顶点的带权连通图,T=(U,TE) G 的最小生成树,则构造最小生成树的步骤如下:
  1) 设置U的初值等于 V (即包含有G中的全部顶点), TE 的初值为空集(即图 T 中每一个顶点都构成一个分量)。
  2) 将图G中的边按权值从小到大的顺序依次选取。若选取的边没有使树 T 生成回路,则将该边加入TE; 否则舍弃,直到 TE 包含 (n1) 条边为止。
  实现克鲁斯卡尔算法的关键是,如何判断选取的边是否与生成树中已保留的边形成回路,这可以通过判断边的两个顶点所在的连通分量的方法来解决。
  为此设置一个辅助数组 vset[0..n1] , 它用于判定两个顶点之间是否连通。数组元素 vset[i] (初值为 i )代表序号为i的顶点所属的连通顶点集的编号(当选中不连通的两个顶点间的一条边时,它们分属的两个顶点集合按其中的一个编号)。当两个顶点的集合编号不同时,加入这两个顶点构成的边到最小生成树时一定不会形成回路。
  采用 E 数组存放图G中的所有边,采用直接插入排序的方法将所有边按权值从小到大的顺序排列。 E 数组元素类型如下:  

typedef struct 
{   int u;              //边的起始顶点
    int v;              //边的终止顶点
    int w;              //边的权值      
} Edge;

Kruskal算法如下:

void SortEdge(MGraph g,Edge E[])    //从邻接矩阵产生权值递增的边集
{
    int i,j,k=0;
    Edge temp;
    for (i=0;i<g.vexnum;i++)
        for (j=0;j<g.vexnum;j++)
            if (g.edges[i][j]<INF)
            {
                E[k].u=i;
                E[k].v=j;
                E[k].w=g.edges[i][j];
                k++;
            }
    for (i=1;i<k;i++)   //按权值递增有序进行直接插入排序 
    {
        temp=E[i];
        j=i-1;          //从右向左在有序区E[0..i-1]中找E[i]的插入位置
        while (j>=0 && temp.w<E[j].w) 
        {   
            E[j+1]=E[j]; //将权值大于E[i].w的记录后移
            j--;
        }
        E[j+1]=temp;      //在j+1处插入E[i]
    }
}
void Kruskal(Edge E[],int n,int e)
{
    int i,j,m1,m2,sn1,sn2,k;
    int vset[MAXE];
    for (i=0;i<n;i++) vset[i]=i;    //初始化辅助数组
    k=1;                            //k表示当前构造最小生成树的第几条边,初值为1
    j=0;                            //E中边的下标,初值为0
    while (k<n)                     //生成的边数小于n时循环
    {   
        m1=E[j].u;m2=E[j].v;        //取一条边的头尾顶点
        sn1=vset[m1];sn2=vset[m2];  //分别得到两个顶点所属的集合编号
        if (sn1!=sn2)               //两顶点属于不同的集合,该边是最小生成树的一条边
        {   
            printf("  (%d,%d):%d\n",m1,m2,E[j].w);
            k++;                    //生成边数增1
            for (i=0;i<n;i++)       //两个集合统一编号
                if (vset[i]==sn2)   //集合编号为sn2的改为sn1
                    vset[i]=sn1;
        }
        j++;                        //扫描下一条边
    }
}

  克鲁斯卡尔算法的时间复杂度为O(eloge), 这个e是图的边数,它的时间复杂度与顶点数 n 无关,只与边数e有关,该算法适用于稀疏图。
  

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
普里姆(Prim)算法克鲁斯卡尔(Kruskal)算法都是用于解决最小生成树问题的算法最小生成树问题是指在一个无向连通图中,找到一棵生成树,使得树上所有边的权值之和最小。生成树是指一个无向图的生成子图,它是一棵树,且包含图中所有顶点。 下面我们分别介绍普里姆算法克鲁斯卡尔算法: 1. 普里姆算法 普里姆算法是一种贪心算法,它从一个任意点开始,逐步扩展生成树,每次选择当前生成树到未加入的点中距离最近的点,并将其加入生成树。 具体实现步骤如下: - 随机选择一个起始点,将其加入生成树。 - 在生成树中的所有节点中,找到到未加入生成树的节点中距离最小的节点,将其加入生成树。 - 重复以上步骤,直到生成树包含了所有节点。 2. 克鲁斯卡尔算法 克鲁斯卡尔算法也是一种贪心算法,它从边集合中选择边,逐步扩展生成树,每次选择当前边集合中权值最小的边,并将其加入生成树。 具体实现步骤如下: - 将所有边按照权值从小到大排序。 - 从权值最小的边开始,逐个加入生成树,如果加入当前边会形成环,则不加入该边。 - 重复以上步骤,直到生成树包含了所有节点。 两种算法的时间复杂度都是O(ElogE),其中E为边数。普里姆算法在处理稠密图时效率更高,而克鲁斯卡尔算法在处理稀疏图时效率更高。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值