算法与数据结构(c语言)——图(Figure,四):最小生成树

最小生成树

普里姆(prim)算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的子集所构成的中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小。

一个百科>>>

描述:从单一顶点开始,普里姆算法按照以下步骤逐步扩大树中所含顶点的数目,直到遍及连通图的所有顶点。

  1. 输入:一个加权连通图,其中顶点集合为V,边集合为E;
  2. 初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {};
  3. 重复下列操作,直到Vnew = V:
    1. 在集合E中选取权值最小的边(u, v),其中u为集合Vnew中的元素,而v则是V中没有加入Vnew的顶点(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
    2. 将v加入集合Vnew中,将(u, v)加入集合Enew中;
  4. 输出:使用集合Vnew和Enew来描述所得到的最小生成树。

时间复杂度

最小边、权的数据结构时间复杂度(总计)
邻接矩阵、搜索O(|V|^{2})
二叉堆(后文伪代码中使用的数据结构)、邻接表{\displaystyle O((|V|+|E|)\log |V|)=O(|E|\log |V|)}
斐波那契堆邻接表{\displaystyle O(|E|+|V|\log |V|)}

通过邻接矩阵图表示的简易实现中,找到所有最小权边共需O(|V|^{2})的运行时间。使用简单的二叉堆邻接表来表示的话,普里姆算法的运行时间则可缩减为{\displaystyle O(|E|\log |V|)},其中|E|为连通图的边集大小,|V|为点集大小。如果使用较为复杂的斐波那契堆,则可将运行时间进一步缩短为{\displaystyle O(|E|+|V|\log |V|)},这在连通图足够密集时(当|E|满足{\displaystyle \Omega (|V|\log |V|)}条件时),可较显著地提高运行速度。

代码实现:

/* Prim算法生成最小生成树  */
void MiniSpanTree_Prim(MGraph G) {
    int min, i, j, k;
    /* 保存相关顶点下标 */
    int adjvex[MAXVN];
    /* 保存相关顶点间边的权值 */
	int lowcost[MAXVN];

    /* 初始化第一个权值为0,即v0加入生成树,lowcost的值为0,在这里就是此下标的顶点已经加入生成树 */
	lowcost[0] = 0;
    /* 初始化第一个顶点下标为0 */
    adjvex[0] = 0;
    /* 循环除下标为0外的全部顶点 */
    for(i = 1; i < G.vertexNum; i++) {
        /* 将v0顶点与之有边的权值存入数组 */
        lowcost[i] = G.arc[0][i];
        /* 初始化都为v0的下标 */
        adjvex[i] = 0;
    }

    for(i = 1; i < G.vertexNum; i++) {
        /* 初始化最小权值为∞,通常设置为不可能的大数字如32767、65535等 */
        min = INFINITY;
        j = 1;
        k = 0;
        /*
        循环全部顶点,找到权值最小的边,同时记住邻接点的索引。
        因为这些个边的就是个邻接矩阵存储,

        而权值是通过循环加到数组中的lowcost数组中,
        lowcost数组中的每一个非0和非∞的元素的下标索引即邻接点的索引。
        */
        while(j < G.vertexNum) {
            /* 如果权值不为0且权值小于min */
            if(lowcost[j] != 0 && lowcost[j] < min) {
                /* 则让当前权值成为最小值 */
                min = lowcost[j];
                /* 将当前最小值的下标存入k */
                k = j;
            }
            j++;
        }
        /* 打印当前顶点边中权值最小的边 */
        printf("edge:(%d, %d), wegiht:%d\n", adjvex[k], k, lowcost[k]);

        /* 将当前顶点的权值设置为0,表示此顶点已经完成任务,
        lowcost数组元素置为0的位置不再加入元素,
        意味着该结点的权值最小的一条边已经确定
        */
        lowcost[k] = 0;

        /* 循环所有顶点,
        通过lowcost中最小元素的下标,
        去将对应的邻接点的所有边权值加入到lowcost数组,
        同时将下标索引加到
        */
        for(j = 1; j < G.vertexNum; j++) {
            /* 如果下标为k顶点各边权值小于此前这些顶点未被加入生成树权值*/
            if(lowcost[j] != 0 && G.arc[k][j] < lowcost[j]) {
                /* 将较小的权值存入lowcost相应位置 */
                lowcost[j] = G.arc[k][j];
                /* 将下标为k的顶点存入adjvex */
                adjvex[j] = k;
            }
        }
    }
}

 运行结果:

然后说下算法的思想: 

从连通网N={V,E}中的某一顶点U0出发,选择与它关联的具有最小权值的边(U0,v),将其顶点加入到生成树的顶点集合U中。以后每一步从一个顶点在U中,而另一个顶点不在U中的各条边中选择权值最小的边(u,v),把它的顶点加入到集合U中。如此继续下去,直到网中的所有顶点都加入到生成树顶点集合U中为止。

还是不太明白的小伙伴,希望本篇文章能对你有帮助>>>戳我

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值