最小生成树的两种方法(Kruskal算法和Prim算法)

原作者:勿在浮沙筑高台http://blog.csdn.net/luoshixian099/article/details/51908175

关于图的几个概念定义:

  • 连通图:在无向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该无向图为连通图。
  • 强连通图:在有向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该有向图为强连通图。
  • 连通网:在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通网。
  • 生成树:一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。
  • 最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。 
    这里写图片描述

下面介绍两种求最小生成树算法

1.Kruskal算法

此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。 
1. 把图中的所有边按代价从小到大排序; 
2. 把图中的n个顶点看成独立的n棵树组成的森林; 
3. 按权值从小到大选择边,所选的边连接的两个顶点ui,viui,vi,应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。 
4. 重复(3),直到所有顶点都在一颗树内或者有n-1条边为止。

这里写图片描述

2.Prim算法

此算法可以称为“加点法”,每次迭代选择代价最小的边对应的点,加入到最小生成树中。算法从某一个顶点s开始,逐渐长大覆盖整个连通网的所有顶点。

  1. 图的所有顶点集合为VV;初始令集合u={s},v=V−uu={s},v=V−u;
  2. 在两个集合u,vu,v能够组成的边中,选择一条代价最小的边(u0,v0)(u0,v0),加入到最小生成树中,并把v0v0并入到集合u中。
  3. 重复上述步骤,直到最小生成树有n-1条边或者n个顶点为止。

由于不断向集合u中加点,所以最小代价边必须同步更新;需要建立一个辅助数组closedge,用来维护集合v中每个顶点与集合u中最小代价边信息,:


 
 
  1. struct
  2. {
  3. char vertexData //表示u中顶点信息
  4. UINT lowestcost //最小代价
  5. }closedge[vexCounts]

这里写图片描述


3.完整代码


 
 
  1. /************************************************************************
  2. CSDN 勿在浮沙筑高台 http://blog.csdn.net/luoshixian099算法导论--最小生成树(Prim、Kruskal)2016年7月14日
  3. ************************************************************************/
  4. #include <iostream>
  5. #include <vector>
  6. #include <queue>
  7. #include <algorithm>
  8. using namespace std;
  9. #define INFINITE 0xFFFFFFFF
  10. #define VertexData unsigned int //顶点数据
  11. #define UINT unsigned int
  12. #define vexCounts 6 //顶点数量
  13. char vextex[] = { 'A', 'B', 'C', 'D', 'E', 'F' };
  14. struct node
  15. {
  16. VertexData data;
  17. unsigned int lowestcost;
  18. }closedge[vexCounts]; //Prim算法中的辅助信息
  19. typedef struct
  20. {
  21. VertexData u;
  22. VertexData v;
  23. unsigned int cost; //边的代价
  24. }Arc; //原始图的边信息
  25. void AdjMatrix(unsigned int adjMat[][vexCounts]) //邻接矩阵表示法
  26. {
  27. for ( int i = 0; i < vexCounts; i++) //初始化邻接矩阵
  28. for ( int j = 0; j < vexCounts; j++)
  29. {
  30. adjMat[i][j] = INFINITE;
  31. }
  32. adjMat[ 0][ 1] = 6; adjMat[ 0][ 2] = 1; adjMat[ 0][ 3] = 5;
  33. adjMat[ 1][ 0] = 6; adjMat[ 1][ 2] = 5; adjMat[ 1][ 4] = 3;
  34. adjMat[ 2][ 0] = 1; adjMat[ 2][ 1] = 5; adjMat[ 2][ 3] = 5; adjMat[ 2][ 4] = 6; adjMat[ 2][ 5] = 4;
  35. adjMat[ 3][ 0] = 5; adjMat[ 3][ 2] = 5; adjMat[ 3][ 5] = 2;
  36. adjMat[ 4][ 1] = 3; adjMat[ 4][ 2] = 6; adjMat[ 4][ 5] = 6;
  37. adjMat[ 5][ 2] = 4; adjMat[ 5][ 3] = 2; adjMat[ 5][ 4] = 6;
  38. }
  39. int Minmum(struct node * closedge) //返回最小代价边
  40. {
  41. unsigned int min = INFINITE;
  42. int index = -1;
  43. for ( int i = 0; i < vexCounts;i++)
  44. {
  45. if (closedge[i].lowestcost < min && closedge[i].lowestcost != 0)
  46. {
  47. min = closedge[i].lowestcost;
  48. index = i;
  49. }
  50. }
  51. return index;
  52. }
  53. void MiniSpanTree_Prim(unsigned int adjMat[][vexCounts], VertexData s)
  54. {
  55. for ( int i = 0; i < vexCounts;i++)
  56. {
  57. closedge[i].lowestcost = INFINITE;
  58. }
  59. closedge[s].data = s; //从顶点s开始
  60. closedge[s].lowestcost = 0;
  61. for ( int i = 0; i < vexCounts;i++) //初始化辅助数组
  62. {
  63. if (i != s)
  64. {
  65. closedge[i].data = s;
  66. closedge[i].lowestcost = adjMat[s][i];
  67. }
  68. }
  69. for ( int e = 1; e <= vexCounts -1; e++) //n-1条边时退出
  70. {
  71. int k = Minmum(closedge); //选择最小代价边
  72. cout << vextex[closedge[k].data] << "--" << vextex[k] << endl; //加入到最小生成树
  73. closedge[k].lowestcost = 0; //代价置为0
  74. for ( int i = 0; i < vexCounts;i++) //更新v中顶点最小代价边信息
  75. {
  76. if ( adjMat[k][i] < closedge[i].lowestcost)
  77. {
  78. closedge[i].data = k;
  79. closedge[i].lowestcost = adjMat[k][i];
  80. }
  81. }
  82. }
  83. }
  84. void ReadArc(unsigned int adjMat[][vexCounts],vector<Arc> &vertexArc) //保存图的边代价信息
  85. {
  86. Arc * temp = NULL;
  87. for ( unsigned int i = 0; i < vexCounts;i++)
  88. {
  89. for ( unsigned int j = 0; j < i; j++)
  90. {
  91. if (adjMat[i][j]!=INFINITE)
  92. {
  93. temp = new Arc;
  94. temp->u = i;
  95. temp->v = j;
  96. temp->cost = adjMat[i][j];
  97. vertexArc.push_back(*temp);
  98. }
  99. }
  100. }
  101. }
  102. bool compare(Arc A, Arc B)
  103. {
  104. return A.cost < B.cost ? true : false;
  105. }
  106. bool FindTree(VertexData u, VertexData v,vector<vector<VertexData> > &Tree)
  107. {
  108. unsigned int index_u = INFINITE;
  109. unsigned int index_v = INFINITE;
  110. for ( unsigned int i = 0; i < Tree.size();i++) //检查u,v分别属于哪颗树
  111. {
  112. if (find(Tree[i].begin(), Tree[i].end(), u) != Tree[i].end())
  113. index_u = i;
  114. if (find(Tree[i].begin(), Tree[i].end(), v) != Tree[i].end())
  115. index_v = i;
  116. }
  117. if (index_u != index_v) //u,v不在一颗树上,合并两颗树
  118. {
  119. for ( unsigned int i = 0; i < Tree[index_v].size();i++)
  120. {
  121. Tree[index_u].push_back(Tree[index_v][i]);
  122. }
  123. Tree[index_v].clear();
  124. return true;
  125. }
  126. return false;
  127. }
  128. void MiniSpanTree_Kruskal(unsigned int adjMat[][vexCounts])
  129. {
  130. vector<Arc> vertexArc;
  131. ReadArc(adjMat, vertexArc); //读取边信息
  132. sort(vertexArc.begin(), vertexArc.end(), compare); //边按从小到大排序
  133. vector< vector<VertexData> > Tree(vexCounts); //6棵独立树
  134. for ( unsigned int i = 0; i < vexCounts; i++)
  135. {
  136. Tree[i].push_back(i); //初始化6棵独立树的信息
  137. }
  138. for ( unsigned int i = 0; i < vertexArc.size(); i++) //依次从小到大取最小代价边
  139. {
  140. VertexData u = vertexArc[i].u;
  141. VertexData v = vertexArc[i].v;
  142. if (FindTree(u, v, Tree)) //检查此边的两个顶点是否在一颗树内
  143. {
  144. cout << vextex[u] << "---" << vextex[v] << endl; //把此边加入到最小生成树中
  145. }
  146. }
  147. }
  148. int main()
  149. {
  150. unsigned int adjMat[vexCounts][vexCounts] = { 0 };
  151. AdjMatrix(adjMat); //邻接矩阵
  152. cout << "Prim :" << endl;
  153. MiniSpanTree_Prim(adjMat, 0); //Prim算法,从顶点0开始.
  154. cout << "-------------" << endl << "Kruskal:" << endl;
  155. MiniSpanTree_Kruskal(adjMat); //Kruskal算法
  156. return 0;
  157. }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值