一、最小生成树的定义

  一个连通图的生成树是一个极小的连通子图,它含有图中全部的顶点,但只有足以构成一棵树的n-1条边。

  在一个网的所有生成树中,权值总和最小的生成树称为最小代价生成树(Minimum Cost Spanning Tree),简称为最小生成树。

  构造最小生成树的准则有以下3条:

  • 只能使用该图中的边构造最小生成树
  • 当且仅当使用n-1条边来连接图中的n个顶点
  • 不能使用产生回路的边

  对比两个算法,Kruskal算法主要是针对边来展开,边数少时效率会非常高,所以对于稀疏图有很大的优势;而Prim算法对于稠密图,即边数非常多的情况会更好一些。

 

  二、普里姆(Prim)算法

  1.Prim算法描述

  假设N={V,{E}}是连通网,TE是N上最小生成树中边的集合。算法从U={u0,u0属于V},TE={}开始。重复执行下面的操作:在所有u属于U,v属于V-U的边(u,v)中找一条代价最小的边(u0,v0)并加入集合TE,同时v0加入U,直到U=V为止。此时TE中必有n-1条边,则T=(V,{TE})为N的最小生成树。

  2.Prim算法的C语言代码实现

/* Prim算法生成最小生成树  */
void MiniSpanTree_Prim(MGraph G)
{
    int min, i, j, k;
    int adjvex[MAXVEX];        /* 保存相关顶点下标 */
    int lowcost[MAXVEX];    /* 保存相关顶点间边的权值 */
    lowcost[0] = 0;/* 初始化第一个权值为0,即v0加入生成树 */
            /* lowcost的值为0,在这里就是此下标的顶点已经加入生成树 */
    adjvex[0] = 0;            /* 初始化第一个顶点下标为0 */
    for(i = 1; i < G.numVertexes; i++)    /* 循环除下标为0外的全部顶点 */
    {
        lowcost[i] = G.arc[0][i];    /* 将v0顶点与之有边的权值存入数组 */
        adjvex[i] = 0;                    /* 初始化都为v0的下标 */
    }
    for(i = 1; i < G.numVertexes; i++)
    {
        min = INFINITY;    /* 初始化最小权值为∞, */
                        /* 通常设置为不可能的大数字如32767、65535等 */
        j = 1;k = 0;
        while(j < G.numVertexes)    /* 循环全部顶点 */
        {
            if(lowcost[j]!=0 && lowcost[j] < min)/* 如果权值不为0且权值小于min */
            {    
                min = lowcost[j];    /* 则让当前权值成为最小值 */
                k = j;            /* 将当前最小值的下标存入k */
            }
            j++;
        }
        printf("(%d, %d)\n", adjvex[k], k);/* 打印当前顶点边中权值最小的边 */
        lowcost[k] = 0;/* 将当前顶点的权值设置为0,表示此顶点已经完成任务 */
        for(j = 1; j < G.numVertexes; j++)    /* 循环所有顶点 */
        {
            if(lowcost[j]!=0 && G.arc[k][j] < lowcost[j]) 
            {/* 如果下标为k顶点各边权值小于此前这些顶点未被加入生成树权值 */
                lowcost[j] = G.arc[k][j];/* 将较小的权值存入lowcost相应位置 */
                adjvex[j] = k;                /* 将下标为k的顶点存入adjvex */
            }
        }
    }
}
Prim算法

相关文章: