我们在图的定义中说过,带有权值的图就是网结构。一个连通图的生成树是一个极小的连通子图,它含有图中全部的顶点,但只有足以构成一棵树的n-1条边。所谓的最小成本,就是n个顶点,用n-1条边把一个连通图连接起来,并且使得权值的和最小。综合以上两个概念,我们可以得出:构造连通网的最小代价生成树,即最小生成树(Minimum Cost Spanning Tree)。

找连通图的最小生成树,经典的有两种算法,普里姆算法和克鲁斯卡尔算法,这里介绍普里姆算法。

为了能够讲明白这个算法,我们先构造网图的邻接矩阵,如图7-6-3的右图所示。

也就是说,现在我们已经有了一个存储结构为MGraph的MG(见《邻接矩阵创建图》)。MG有9个顶点,它的二维数组如右图所示,数组中我们使用65535代表无穷。

下面我们对着程序和每一步循环的图示来看:

算法代码:(改编自《大话数据结构》)

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
 
/* Prim算法生成最小生成树  */
void MiniSpanTree_Prim(MGraph MG)
{
    int min, i, j, k;
    int adjvex[MAXVEX];/* 保存相关顶点下标 */
    int lowcost[MAXVEX];/* 保存相关顶点间边的权值 */
    lowcost[0] = 0;/* 初始化第一个权值为0,即v0加入生成树 */
    /* lowcost的值为0,在这里就是此下标的顶点已经加入生成树 */
    adjvex[0] = 0;/* 初始化第一个顶点下标为0 */
    cout << "最小生成树的边为:" << endl;
    for (i = 1; i < MG.numVertexes; i++)
    {
        lowcost[i] = MG.arc[0][i];/* 将v0顶点与之有边的权值存入数组 */
        adjvex[i] = 0;/* 初始化都为v0的下标 */
    }

for (i = 1; i < MG.numVertexes; i++)
    {
        min = INFINITY; /* 初始化最小权值为∞, */

j = 1;
        k = 0;

while (j < MG.numVertexes)/* 循环全部顶点 */
        {
            if (lowcost[j] != 0 && lowcost[j] < min)/* 如果权值不为0且权值小于min */
            {
                min = lowcost[j];/* 则让当前权值成为最小值 */
                k = j;/* 将当前最小值的下标存入k */
            }

j++;
        }

cout << "(" << adjvex[k] << ", " << k << ")" << "  "; /* 打印当前顶点边中权值最小的边 */
        lowcost[k] = 0;/* 将当前顶点的权值设置为0,表示此顶点已经完成任务 */

for (j = 1; j < MG.numVertexes; j++)/* 循环所有顶点 */
        {
            /* 如果下标为k顶点各边权值小于此前这些顶点未被加入生成树权值 */
            if (lowcost[j] != 0 && MG.arc[k][j] < lowcost[j])
            {
                lowcost[j] = MG.arc[k][j];/* 将较小的权值存入lowcost相应位置 */
                adjvex[j] = k;/* 将下标为k的顶点存入adjvex */
            }
        }
    }
    cout << endl;
}

1、程序中1~16行是初始化操作,其中第7~8行 adjvex[0] = 0 意思是现在从顶点v0开始(事实上从那一点开始都无所谓,假定从v0开始),lowcost[0]= 0 表示v0已经被纳入到最小生成树中,之后凡是lowcost数组中的值被设为0就表示此下标的顶点被纳入最小生成树。

2、第11~15行表示读取邻接矩阵的第一行数据,所以 lowcost数组为{ 0 ,10, 65535, 65535, 65535, 11, 65535, 65535, 65535 },而adjvex数组为全0。至此初始化完毕。

3、第17~49行共循环了8次,i从1一直累加到8,整个循环过程就是构造最小生成树的过程。

4、第24~33行,经过循环后min = 10, k = 1。注意26行的if 判断lowcost[j] != 0 表示已经是生成树的顶点则不参加最小权值的查找。

5、第35行,因k = 1, adjvex[1] = 0, 所以打印结果为(0, 1),表示v0 至 v1边为最小生成树的第一条边,如下图的第一个小图。

6、第36行,因k = 1 将lowcost[k] = 0 就是说顶点v1纳入到最小生成树中,此时lowcost数组为{ 0,0, 65535, 65535, 65535, 11, 65535, 65535, 65535 }

7、第38~47行,j 循环从1 到8, 因k = 1,查找邻接矩阵的第v1行的各个权值,与lowcost数组对应值比较,若更小则修改lowcost值,并将k值存入adjvex数组中。所以最终lowcost = { 0,0, 18, 65535, 65535, 11, 16, 65535, 12 }。 adjvex数组的值为 {0, 0, 1, 0, 0, 0, 1, 0,
1 }。这里的if判断也表示v0和v1已经是生成树的顶点不参与最小权值的比对了。

上面所述为第一次循环,对应下图i = 1的第一个小图,由于要用文字描述清楚整个流程比较繁琐,下面给出i为不同值一次循环下来后的生成树图示,所谓一图值千言,大家对着图示自己模拟地循环8次就能理解普里姆算法的思想了。

即最小生成树的边为:(0, 1), (0, 5), (1, 8), (8, 2), (1, 6), (6, 7), (7, 4), (7, 3)

最后再来总结一下普里姆算法的定义:

假设N = (V{E} )是连通网,TE是N上最小生成树的集合。算法从U = { u0} ( uo V),TE = { } 开始。重复执行下述操作:在所有

uU,v V - U 的边(u, v) E 中找一条代价最小的边(u0 , v0)
并入集合TE, 同时v0 并入U, 直至 U = V 为止。此时TE 中必有n-1 条边, 则 T = (V,{TE} ) 为N的最小生成树。

由算法代码中的循环嵌套可得知此算法的时间复杂度为O(n^2)。

对比普里姆和克鲁斯卡尔算法,克鲁斯卡尔算法主要针对边来展开,边数少时效率比较高,所以对于稀疏图有较大的优势;而普里姆算法对于稠密图,即边数非常多的情况下更好一些。

图解最小生成树 - 普里姆(Prim)算法的更多相关文章

  1. 普里姆Prim算法介绍

    普里姆(Prim)算法,和克鲁斯卡尔算法一样,是用来求加权连通图的最小生成树的算法. 基本思想 对于图G而言,V是所有顶点的集合:现在,设置两个新的集合U和T,其中U用于存放G的最小生成树中的顶点,T ...

  2. 最小生成树-普利姆(Prim)算法

    最小生成树-普利姆(Prim)算法 最小生成树 概念:将给出的所有点连接起来(即从一个点可到任意一个点),且连接路径之和最小的图叫最小生成树.最小生成树属于一种树形结构(树形结构是一种特殊的图),或者 ...

  3. 图论---最小生成树----普利姆(Prim)算法

    普利姆(Prim)算法 1. 最小生成树(又名:最小权重生成树) 概念:将给出的所有点连接起来(即从一个点可到任意一个点),且连接路径之和最小的图叫最小生成树.最小生成树属于一种树形结构(树形结构是一 ...

  4. JS实现最小生成树之普里姆(Prim)算法

    最小生成树: 我们把构造连通网的最小代价生成树称为最小生成树.经典的算法有两种,普利姆算法和克鲁斯卡尔算法. 普里姆算法打印最小生成树: 先选择一个点,把该顶点的边加入数组,再按照权值最小的原则选边, ...

  5. 图的普里姆(Prim)算法求最小生成树

    关于图的最小生成树算法------普里姆算法 首先我们先初始化一张图: 设置两个数据结构来分别代表我们需要存储的数据: lowcost[i]:表示以i为终点的边的最小权值,当lowcost[i]=0说 ...

  6. 普里姆(Prim)算法

    /* 普里姆算法的主要思想: 利用二维数组把权值放入,然后找在当前顶点的最小权值,然后走过的路用一个数组来记录 */ # include <stdio.h> typedef char Ve ...

  7. 图->连通性->最小生成树(普里姆算法)

    文字描述 用连通网来表示n个城市及n个城市间可能设置的通信线路,其中网的顶点表示城市,边表示两城市之间的线路,赋于边的权值表示相应的代价.对于n个定点的连通网可以建立许多不同的生成树,每一棵生成树都可 ...

  8. 图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

    图的连通性问题:无向图的连通分量和生成树,所有顶点均由边连接在一起,但不存在回路的图. 设图 G=(V, E) 是个连通图,当从图任一顶点出发遍历图G 时,将边集 E(G) 分成两个集合 T(G) 和 ...

  9. 经典问题----最小生成树(prim普里姆贪心算法)

    题目简述:假如有一个无向连通图,有n个顶点,有许多(带有权值即长度)边,让你用在其中选n-1条边把这n个顶点连起来,不漏掉任何一个点,然后这n-1条边的权值总和最小,就是最小生成树了,注意,不可绕成圈 ...

随机推荐

  1. JQuery模仿淘宝天猫魔盒抢购页面倒计时效果

    1.效果及功能说明 通过对时间的控制来告诉用户一个活动还剩多少时间,精确到秒.2.实现原理 首先定义活动的截至的时间,要重年份精确到毫秒,在获得当前的年份到秒钟,在用截至时间,减去现在的时间,剩下的还 ...

  2. Back Track 5 之 漏洞攻击 && 密码攻击 && Windows下渗透工具

    网络漏洞攻击工具 Metasploit 先msfupdate升级: 然后选择msfconsole: 接下来: set LHOST 本机IP地址 setLPORT setg PAYLOAD window ...

  3. Android直播实现 Android端推流、播放

    最近想实现一个Android直播,但是对于这方面的资料都比较零碎,一开始是打算用ffmpeg来实现编码推流,在搜集资料期间,找到了几个强大的开源库,直接避免了jni的代码,集成后只用少量的java代码 ...

  4. Jmeter-Maven-Plugin高级应用:Test Results File Format-Test Results

    Test Results File Format Test Results Disabling The <testResultsTimestamp> Enabling <append ...

  5. 【Window OS】”对于目标文件系统,文件XXXXX过大“导致无法进行文件操作的解决方法

    问题原因:这是目标文件系统不支持这么大的文件的操作问题.例如:目标文件系统的格式是FAT32,FAT32最大支持4G,如果你要进行发送或粘贴4G以上的文件就会出现这个问题. 解决办法:把目标文件系统的 ...

  6. 不兼容:不支持SCSI硬盘

    获取机器硬件失败,可能你使用了SCSI硬盘,请更换一台主机进行安装 聚生网管2.11版本不支持scsi硬盘. 终于体会到了不兼容的麻烦了.

  7. Firefox 之 应用小结

    1. 调试脚本       做前端开发的朋友应该对FireFox再熟悉不过了,FireFox有一个附加组件FireBug.在HTML中可以直接写   <script type="tex ...

  8. wepy - 与原生有什么不同($pages,$interceptors)

    wepy内部封装的一些基类,我们要注意以 “$”开头命名,最好不用 关于wepy基类文档,请查看 关于$apply,其实就是主动刷新DOM,来更新数据. 何时使用它? 答. 你为data里面的数据进行 ...

  9. PHP MySQL -处理语句

    PHP MySQL 预处理语句 预处理语句对于防止 MySQL 注入是非常有用的. 预处理语句及绑定参数 预处理语句用于执行多个相同的 SQL 语句,并且执行效率更高. 预处理语句的工作原理如下: 预 ...

  10. 策略模式(Strategy)简介

    一.策略模式(Strategy)简介 策略模式是行为模式. 行为模式:规定了各个对象应该具备的职责以及对象间的通信模式,它很好的规范了对象间调用和数据传递方式 策略模式适合于算法经常变化的情况 算法的 ...