当前位置:   article > 正文

最小生成树之普里姆算法_已知一带权连通图如图所示。根据prim算法求出其所有可能的最小生成树

已知一带权连通图如图所示。根据prim算法求出其所有可能的最小生成树

一、最小生成树

1、如下图所示,假如V0~V8表示9个不同的村庄,现在要在这9个村庄中搭建通信网络,其中的权值表示村庄与村庄之间的可通达的直线距离,没有连线的表示不予考虑,距离越大成本就越高,要求用最小的成本完成该任务。那么该在这9个村庄中怎么布局呢。


  这是一个带权值的图,即网图,所谓的最小成本就是n个顶点,用n-1条边把一个连通图连接起来,且使得权值的和最小,所以在该图中只要连接的距离最少,成本就最少了。如下有几种方案:

  • 方案一:如下图所示,所需成本为11+26+20+24+19+21+22+18=161

  • 方案二:如下图所示,所需成本为10+12+8+11+17+19+16+7=100

  • 方案三:如下图所示,所需成本为8+12+16+10+11+19+16+7=99

  其实这就是求图的最小生成树的问题,上面的三个方案都是该网图的生成树,但是只有方案三才是最小生成树,关键是如何求的图的最小生成树呢?
二、普里姆算法
1、如下图所示,用普里姆算法求该图的最小生成树的过程为:


  • 第一步:选择一个出发点,比如V0,将其背景色由白色变成黑色:

  • 第二步:把结点V0与相邻结点间的边变成紫色:

  • 第三步:在紫色的边中选取一条权值最小的边,将此边变成红色,相邻结点变色黑色:




  • 第四步:将黑色顶点与白色顶点间的边变成紫色,若一个白色顶点与多个黑色顶点有边相连,则选取权值最小的边变成紫色,其它的边变成灰色,表示不考虑

  • 第五步:在紫色边中选择一条权值最小的边,将此边变成红色,相邻结点变成黑色:

  • 第六步:将黑色顶点与白色顶点间的边变成紫色,若一个白色顶点与多个黑色顶点有边相连,则选取权值最小的边变成紫色,其它的边变成灰色,表示不考虑

  • 第七步:在紫色边中选择一条权值最小的边,将此边变成红色,相邻顶点变成黑色:

  • 第八步:在紫色边中选择一条权值最小的边,将此边变成红色,相邻顶点变成黑色:

  • 第九步:将黑色顶点与白色顶点间的边变成紫色,若一个白色顶点与多个黑色顶点有边相连,则选取权值最小的边变成紫色,其它的边变成灰色,表示不考虑:

  • 第十步:在紫色边中选择一条权值最小的边,将此边变成红色,相邻顶点变成黑色。普里姆算法构造最小生成树结束,黑色顶点和红色边组成的树就是最小生成树:

  从以过程 中可以发 现,整个过程其实就是不断循环第二步和第三步,直到最后一个顶点为止。
2、完整实现代码如下:
  1. /*********************************************************************/
  2. /** 最小生成树之普里姆算法(邻接矩阵存储图) **/
  3. /*********************************************************************/
  4. #include <stdio.h>
  5. #define GRAPH_MAX_VERTEX_SIZE 100 // 最大顶点数
  6. #define INFINITY 65535 // 用65535来代表∞,在这里代表村庄之间没有可达路径
  7. typedef char VertexType; // 顶点类型
  8. typedef int EdgeType; // 边上的权值类型
  9. typedef struct Graph
  10. {
  11. VertexType vexs[GRAPH_MAX_VERTEX_SIZE]; // 顶点表
  12. EdgeType arc[GRAPH_MAX_VERTEX_SIZE][GRAPH_MAX_VERTEX_SIZE]; // 邻接矩阵
  13. int numVertexs, numEdges; // 图中当前的顶点数和边数
  14. }Graph;
  15. /**
  16. * 建立无向网图的邻接矩阵表示
  17. * @param graph:指向图结构的指针
  18. */
  19. void CreateGraph(Graph *graph)
  20. {
  21. int i, j, k, w;
  22. printf("输入顶点数和边数,分别用空格分隔:");
  23. scanf("%d %d", &(graph->numVertexs), &(graph->numEdges)); // 接收输入的顶点数和边数
  24. for(i = 0; i < graph->numVertexs; i++) // 读入顶点信息,建立顶点表
  25. {
  26. printf("输入第%d个顶点信息:", i + 1);
  27. fflush(stdin); // 清空键盘输入缓冲区
  28. scanf("%c", &(graph->vexs[i]));
  29. }
  30. for(i = 0; i < graph->numVertexs; i++)
  31. {
  32. for(j = 0; j < graph->numVertexs; j++)
  33. {
  34. graph->arc[i][j] = INFINITY; // 邻接矩阵初始化
  35. }
  36. }
  37. for(k = 0; k < graph->numEdges; k++) // 读入numEdges条边,建立邻接矩阵
  38. {
  39. printf("输入边(vi,vj)上的下标i,下标j和权w,分别用空格分隔:");
  40. fflush(stdin); // 清空键盘输入缓冲区
  41. scanf("%d %d %d", &i, &j, &w);
  42. graph->arc[i][j] = w;
  43. graph->arc[j][i] = graph->arc[i][j]; // 因为是无向图,矩阵对称
  44. }
  45. }
  46. /**
  47. * 普里姆算法生成最小生成树
  48. */
  49. void Prim(Graph graph)
  50. {
  51. int min, i, j, k;
  52. int adjvex[GRAPH_MAX_VERTEX_SIZE]; // 保存相关顶点下标
  53. int lowcost[GRAPH_MAX_VERTEX_SIZE]; // 保存相关顶点之间边的权值(成本)
  54. adjvex[0] = 0; // V0第一个加入
  55. lowcost[0] = 0; // V0作为最小生成树的根开始遍历,权值为0
  56. // 初始化
  57. for(i = 1; i < graph.numVertexs; i++)
  58. {
  59. lowcost[i] = graph.arc[0][i]; // 将邻接矩阵第0行所有权值加入数组中
  60. adjvex[i] = 0; // 初始化全部先为V0的下标
  61. }
  62. // 构造最小生成树
  63. for(i = 1; i < graph.numVertexs; i++)
  64. {
  65. min = INFINITY; // 初始化最小权值为65535等不可能的数值
  66. j = 1;
  67. k = 0;
  68. // 遍历全部顶点
  69. while(j < graph.numVertexs)
  70. {
  71. // 找出lowcost数组已存储的最小权值
  72. if(lowcost[j] != 0 && lowcost[j] < min)
  73. {
  74. min = lowcost[j];
  75. k = j; // 将发现的最小权值的下标赋值给k
  76. }
  77. j++;
  78. }
  79. // 打印当前顶点边中权值最小的边
  80. printf("(%d, %d)", adjvex[k], k);
  81. lowcost[k] = 0; // 将当前顶点的权值设为0,表示此顶点已完成任务,进入下一个顶点的遍历
  82. // 邻接矩阵k行逐个遍历全部顶点
  83. for(j = 1; j <graph.numVertexs; j++)
  84. {
  85. if(lowcost[j] != 0 && graph.arc[k][j] < lowcost[j])
  86. {
  87. lowcost[j] = graph.arc[k][j];
  88. adjvex[j] = k;
  89. }
  90. }
  91. }
  92. }
  93. int main()
  94. {
  95. Graph graph;
  96. CreateGraph(&graph);
  97. Prim(graph);
  98. return 0;
  99. }

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号