给定一个带权值的无向图,要求权值之和最小的生成树,常用的算法有Kruskal算法和Prim算法。这两个算法其实都是贪心思想的使用,但又能求出最优解。(代码借鉴http://blog.csdn.net/u014488381)

一.Kruskal算法

Kruskal算法的基本思想:先将所有边按权值从小到大排序,然后按顺序选取每条边,假如一条边的两个端点不在同一个集合中,就将这两个端点合并到同一个集合中;假如两个端点在同一个集合中,说明这两个端点已经连通了,就将当前这条边舍弃掉;当所有顶点都在同一个集合时,说明最小生成树已经形成。(写代码的时候会将所有边遍历一遍)

来看一个例子:

步骤:

(1)先根据权值把边排序:

AD 5

CE 5

DF 6

AB 7

BE 7

BC 8

EF 8

BD 9

EG 9

FG 11

(2)

选择AD这条边,将A、D加到同一个集合1中

选择CE这条边,将C、E加到同一个集合2中(不同于AD的集合)

选择DF这条边,由于D已经在集合1中,因此将F加入到集合1中,集合变为A、D、F

选择AB这条边,同理,集合1变为A、B、D、F

选择BE这条边,由于B在集合1中,E在集合2中,因此将两个集合合并,形成一个新的集合ABCDEF

由于E、F已经在同一集合中,舍弃掉BC这条边;同理舍弃掉EF、BD

选择EG这条边,此时所有元素都已经在同一集合中,最小生成树形成

象征性地舍弃掉FG这条边

实现代码如下:

  1. #include <iostream>
  2. #include <cstring>
  3. #define MaxSize 20
  4. using namespace std;
  5.  
  6. struct Edge{
  7. int begin;
  8. int end;
  9. int weight;
  10. };
  11. struct Graph{
  12. char ver[MaxSize + ];
  13. int edg[MaxSize][MaxSize];
  14. };
  15.  
  16. void CreateGraph(Graph *g) {
  17. int VertexNum;
  18. char Ver;
  19. int i = ;
  20. cout << "输入图的顶点:" << endl;
  21. while ((Ver = getchar()) != '\n') {
  22. g->ver[i] = Ver;
  23. i++;
  24. }
  25. g->ver[i] = '\0';
  26. VertexNum = strlen(g->ver);
  27. cout << "输入相应的邻接矩阵" << endl;
  28. for (int i = ; i < VertexNum; i++) {
  29. for (int j = ; j < VertexNum; j++) {
  30. cin >> g->edg[i][j]; //输入0则为没有边相连啊
  31. }
  32. }
  33. }
  34.  
  35. void PrintGraph(Graph g) {
  36. int VertexNum = strlen(g.ver);
  37. cout << "图的顶点为:" << endl;
  38. for (int i = ; i < VertexNum; i++) {
  39. cout << g.ver[i] << " ";
  40. }
  41. cout << endl;
  42. cout << "图的邻接矩阵为:" << endl;
  43. for (int i = ; i < VertexNum; i++) {
  44. for (int j = ; j < VertexNum; j++) {
  45. cout << g.edg[i][j] << " ";
  46. }
  47. cout << endl;
  48. }
  49. }
  50.  
  51. int getVerNum(Graph g) {
  52. return strlen(g.ver);
  53. }
  54.  
  55. int getEdgeNum(Graph g) {
  56. int res = ;
  57. int VertexNum = getVerNum(g);
  58. for (int i = ; i < VertexNum; i++) {
  59. //邻接矩阵对称,计算上三角元素和即可
  60. for (int j = i + /*假设没有自己指向自己的*/; j < VertexNum; j++) {
  61. if (g.edg[i][j] != ) res++;
  62. }
  63. }
  64. return res;
  65. }
  66.  
  67. Edge *CreateEdges(Graph g) {
  68. int k = ;
  69. int EdgeNum = getEdgeNum(g);
  70. int VertexNum = getVerNum(g);
  71. Edge * p = new Edge[EdgeNum];
  72. for (int i = ; i < VertexNum; i++) {
  73. for (int j = i; j < VertexNum; j++) {
  74. if (g.edg[i][j] != ) {
  75. p[k].begin = i;
  76. p[k].end = j;
  77. p[k].weight = g.edg[i][j];
  78. k++;
  79. }
  80. }
  81. }
  82. for (int i = ; i < EdgeNum - ; i++) {
  83. Edge minWeightEdge = p[i];
  84. for (int j = i + ; j < EdgeNum; j++) {
  85. if (minWeightEdge.weight > p[j].weight) {
  86. Edge temp = minWeightEdge;
  87. minWeightEdge = p[j];
  88. p[j] = temp;
  89. }
  90. }
  91. p[i] = minWeightEdge;
  92. }
  93. return p;
  94. }
  95.  
  96. void Kruskal(Graph g) {
  97. int VertexNum = getVerNum(g);
  98. int EdgeNum = getEdgeNum(g);
  99. Edge *p = CreateEdges(g);
  100. int *index = new int[VertexNum]; //index数组,其元素为连通分量的编号,index[i]==index[j]表示编号为i和j的顶点在同一连通分量中
  101. int *MSTEdge = new int[VertexNum - ]; //用来存储已确定的最小生成树的**边的编号**,共VertexNum-1条边
  102. int k = ;
  103. int WeightSum = ;
  104. int IndexBegin, IndexEnd;
  105. for (int i = ; i < VertexNum; i++) {
  106. index[i] = -; //初始化所有index为-1
  107. }
  108. for (int i = ; i < VertexNum - ; i++) {
  109. for (int j = ; j < EdgeNum; j++) {
  110. if ( !(index[p[j].begin] >= && index[p[j].end] >= && index[p[j].begin] == index[p[j].end] /*若成立表明p[j].begin和p[j].end已在同一连通块中(且可相互到达,废话)*/) ) {
  111. MSTEdge[i] = j;
  112. if (index[p[j].begin] == - && index[p[j].end] == -) {
  113. index[p[j].begin] = index[p[j].end] = i;
  114. }
  115. else if (index[p[j].begin] == - && index[p[j].end] >= ) {
  116. index[p[j].begin] = i;
  117. IndexEnd = index[p[j].end];
  118. for (int n = ; n < VertexNum; n++) {
  119. if (index[n] == IndexEnd) {
  120. index[n] == i;
  121. }
  122. }
  123. }
  124. else if (index[p[j].begin] >= && index[p[j].end] == -) {
  125. index[p[j].end] = i;
  126. IndexBegin = index[p[j].begin];
  127. /*将连通分量合并(或者说将没加入连通分量的顶点加进去,然后将原来连通分量的值改了)*/
  128. for (int n = ; n < VertexNum; n++) {
  129. if (index[n] == IndexBegin) {
  130. index[n] == i;
  131. }
  132. }
  133. }
  134. else {
  135. IndexBegin = index[p[j].begin];
  136. IndexEnd = index[p[j].end];
  137. for (int n = ; n < VertexNum; n++) {
  138. if (index[n] == IndexBegin || index[n] == IndexEnd) {
  139. index[n] = i;
  140. }
  141. }
  142. }
  143. break;
  144. }
  145. }
  146. }
  147. cout << "MST的边为:" << endl;
  148. for (int i = ; i < VertexNum - ; i++) {
  149. cout << g.ver[p[MSTEdge[i]].begin] << "--" << g.ver[p[MSTEdge[i]].end] << endl;
  150. WeightSum += p[MSTEdge[i]].weight;
  151. }
  152. cout << "MST的权值为:" << WeightSum << endl;
  153. }

二.Prim算法(代码还没理解)

Prim算法的基本思想:设置两个存放顶点的集合,第一个集合初始化为空,第二个集合初始化为一个包含所有顶点的集合。首先把图中的任意一个顶点a放进第一个集合,然后在第二个集合中找到一个顶点b,使b到第一个集合中的任意一点的权值最小,然后把b从第二个集合移到第一个集合。接着在第二个集合中找到顶点c,使c到a或b的权值比到第二个集合中的其他任何顶点到a或b的权值都要小,然后把c从第二个集合移到第一个集合中。以此类推,当第二个集合中的顶点全部移到第一个集合时,最小生成树产生。

以上面的图再次作为例子:

设第一个集合为V,第二个集合为U。

V={A}, U={B, C, D, E, F, G}

(1)A连接了两个顶点,B和D,AB权值为7,AD权值为5,选择权值小的一条边和相应的顶点D,将D加入集合V中。V={A, D}, U={B, C, E, F, G}

(2)观察包含V中的元素A和D的边,AB权值为7,BD权值为9,DE权值为15,DF权值为6,将F加入V中。V={A, D, F}, U={B, C, E, G}

(3)依次将B(AB)、E(BE)、C(CE)、G(EG)加入到集合V中。

(4)最小生成树的边包括:AD DF AB BE CE EG,problem solved

实现代码如下:

  1. #include <iostream>
  2. #include <vector>
  3. #include <cstring>
  4. using namespace std;
  5. #define MaxSize 20
  6. struct Graph{
  7. char ver[MaxSize + ];
  8. int edg[MaxSize][MaxSize];
  9. };
  10.  
  11. void CreateGraph(Graph *g) {
  12. int VertexNum;
  13. char Ver;
  14. int i = ;
  15. cout << "输入图的顶点:" << endl;
  16. while ((Ver = getchar()) != '\n') {
  17. g->ver[i] = Ver;
  18. i++;
  19. }
  20. g->ver[i] = '\0';
  21. VertexNum = strlen(g->ver);
  22. cout << "输入相应的邻接矩阵" << endl;
  23. for (int i = ; i < VertexNum; i++) {
  24. for (int j = ; j < VertexNum; j++) {
  25. cin >> g->edg[i][j]; //输入0则为没有边相连啊
  26. }
  27. }
  28. }
  29.  
  30. void PrintGraph(Graph g) {
  31. int VertexNum = strlen(g.ver);
  32. cout << "图的顶点为:" << endl;
  33. for (int i = ; i < VertexNum; i++) {
  34. cout << g.ver[i] << " ";
  35. }
  36. cout << endl;
  37. cout << "图的邻接矩阵为:" << endl;
  38. for (int i = ; i < VertexNum; i++) {
  39. for (int j = ; j < VertexNum; j++) {
  40. cout << g.edg[i][j] << " ";
  41. }
  42. cout << endl;
  43. }
  44. }
  45.  
  46. int getVerNum(Graph g) {
  47. return strlen(g.ver);
  48. }
  49.  
  50. //将不邻接的顶点之间的权值设为
  51. void SetWeight(Graph *g) {
  52. for (int i = ; i < getVerNum(*g); i++) {
  53. for (int j = ; j < getVerNum(*g); j++) {
  54. if (g->edg[i][j] == ) {
  55. g->edg[i][j] = INT_MAX;
  56. }
  57. }
  58. }
  59. }
  60.  
  61. void Prim(Graph g, int *parent) {
  62. //V为所有顶点的集合,U为最小生成树的节点集合
  63. int lowcost[MaxSize]; //lowcost[k]保存着编号为k的顶点到U中所有顶点的最小权值
  64. int closest[MaxSize]; //closest[k]保存着U到V-U中编号为k的顶点权值最小的顶点的编号
  65. int used[MaxSize];
  66. int min;
  67. int VertexNum = getVerNum(g);
  68. for (int i = ; i < VertexNum; i++) {
  69. lowcost[i] = g.edg[][i];
  70. closest[i] = ;
  71. used[i] = ;
  72. parent[i] = -;
  73. }
  74. used[] = ;
  75. for (int i = ; i < VertexNum - ; i++) {
  76. int j = ;
  77. min = INT_MAX;
  78. for (int k = ; k < VertexNum; k++) { //找到V-U中的与U中顶点组成的最小权值的边的顶点编号
  79. if (used[k] == && lowcost[k] < min) {
  80. min = lowcost[k];
  81. j = k;
  82. }
  83. }
  84. parent[j] = closest[j];
  85. used[j] = ;
  86. for (int k = ; k < VertexNum; k++) { //由于j顶点加入U中,更新lowcost和closest数组中的元素,检测V-U中的顶点到j顶点的权值是否比j加入U之前的lowcost数组的元素小
  87. if (used[k] == && g.edg[j][k] < lowcost[k]) {
  88. lowcost[k] = g.edg[j][k];
  89. closest[k] = j;
  90. }
  91. }
  92. }
  93. }
  94.  
  95. void PrintMST(Graph g, int *parent) {
  96. int VertexNum = getVerNum(g);
  97. int weight = ;
  98. cout << "MST的边为:" << endl;
  99. for (int i = ; i < VertexNum; i++) {
  100. cout << g.ver[parent[i]] << "--" << g.ver[i] << endl;
  101. weight += g.edg[parent[i]][i];
  102. }
  103. cout << "MST的权值为" << weight << endl;
  104. }
  105.  
  106. int main() {
  107. Graph g;
  108. int parent[];
  109. CreateGraph(&g);
  110. PrintGraph(g);
  111. SetWeight(&g);
  112. Prim(g, parent);
  113. PrintMST(g, parent);
  114. return ;
  115. }

三.Kruskal算法和Prim算法的适用情况

Kruskal算法适用于边稀疏的情况(要进行排序),Prim算法适用于边稠密的情况。

求最小生成树——Kruskal算法的更多相关文章

  1. SWUST OJ 1075 求最小生成树(Prim算法)

    求最小生成树(Prim算法) 我对提示代码做了简要分析,提示代码大致写了以下几个内容 给了几个基础的工具,邻接表记录图的一个的结构体,记录Prim算法中最近的边的结构体,记录目标边的结构体(始末点,值 ...

  2. 【转】最小生成树——Kruskal算法

    [转]最小生成树--Kruskal算法 标签(空格分隔): 算法 本文是转载,原文在最小生成树-Prim算法和Kruskal算法,因为复试的时候只用到Kruskal算法即可,故这里不再涉及Prim算法 ...

  3. 求最小生成树——Kruskal算法和Prim算法

    给定一个带权值的无向图,要求权值之和最小的生成树,常用的算法有Kruskal算法和Prim算法.这两个算法其实都是贪心思想的使用,但又能求出最优解.(代码借鉴http://blog.csdn.net/ ...

  4. 最小生成树 kruskal算法&prim算法

    (先更新到这,后面有时间再补,嘤嘤嘤) 今天给大家简单的讲一下最小生成树的问题吧!(ps:本人目前还比较菜,所以最小生成树最后的结果只能输出最小的权值,不能打印最小生成树的路径) 本Tianc在刚学的 ...

  5. 数据结构之最小生成树Kruskal算法

    1. 克鲁斯卡算法介绍 克鲁斯卡尔(Kruskal)算法,是用来求加权连通图的最小生成树的算法. 基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路. 具体做法:首先构造一个 ...

  6. 数据结构:最小生成树--Kruskal算法

    Kruskal算法 Kruskal算法 求解最小生成树的还有一种常见算法是Kruskal算法.它比Prim算法更直观.从直观上看,Kruskal算法的做法是:每次都从剩余边中选取权值最小的,当然,这条 ...

  7. 图的最小生成树——Kruskal算法

    Kruskal算法 图的最小生成树的算法之一,运用并查集思想来求出最小生成树. 基本思路就是把所有边从小到大排序,依次遍历这些边.如果这条边所连接的两个点在一个连通块里,遍历下一条边,如果不在,就把这 ...

  8. 【一个蒟蒻的挣扎】最小生成树—Kruskal算法

    济南集训第五天的东西,这篇可能有点讲不明白提前抱歉(我把笔记忘到别的地方了 最小生成树 概念:一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的 ...

  9. 最小生成树Kruskal算法(1)

    概念 一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边. [1] 最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆) ...

随机推荐

  1. 201521123054 《Java程序设计》 第十周学习总结

    1. 本周学习总结 2. 书面作业 题目4-2 1.1 截图你的提交结果(出现学号) 1.2 4-2中finally中捕获异常需要注意什么? 无论是否抛出异常,也无论从什么地方返回,finally语句 ...

  2. Linux-exec命令试验驱动(12)

    对于做驱动经常会使用exec来试验驱动,通过exec将-sh进程下的描述符指向我们的驱动,来实现调试 -sh进程常用描述符号: 0:标准输入 1:标准输出 2:错误信息 5:中断服务 exec命令使用 ...

  3. 在有main函数的前提下 eclipse找不到主类

    有时候在测试类的时候eclipse会莫名奇妙的提示找不到主类   接下来分别有几种解决办法 1.在项目上右击> Builder Path -> Configure Build Path - ...

  4. Excel 若某列包含某个字符则特定列显示特定字符

    今天在写Excel , 有很多重复的数据, 在想 如果 可以像Java  一样 筛选就好了 这样的效果 if ("adj".equals(sheet1.A1)){ sheet1.B ...

  5. 微软云Linux服务器 Mysql、tomcat远程连接错误解决办法

    在微软云linux服务器成功配置好mysql.tomcat,通过外部链接却发现一直错误.Mysql 一直提示错误代码2003, tomcat连接一直提示EOF. 反复检查配置都无问题,最后得知是微软云 ...

  6. JAVA_String、StringBuilder、StringBuffer区别

    String.StringBuilder.StringBuffer均为字符串 类 需要注意的一些问题 String StringBuilder StringBuffer 一旦创建,不能对其内容进行更改 ...

  7. day12<常见对象+>

    常见对象(Scanner的概述和方法介绍) 常见对象(Scanner获取数据出现的小问题及解决方案) 常见对象(String类的概述) 常见对象(String类的构造方法) 常见对象(String类的 ...

  8. Ubuntu17.04安装wps

    1.进入http://community.wps.cn/download/下载wps-office安装包,我下载的是dep的包. 2.进行安装执行命令:sudo dpkg -i wps-office_ ...

  9. java 对象转型

    一.对象转型介绍 对象转型分为两种:一种叫向上转型(父类对象的引用或者叫基类对象的引用指向子类对象,这就是向上转型),另一种叫向下转型.转型的意思是:如把float类型转成int类型,把double类 ...

  10. EntityFramework Core饥饿加载忽略导航属性问题

    前言 .NET Core项目利用EntityFramework Core作为数据访问层一直在进行中,一直没有过多的去关注背后生成的SQL语句,然后老大捞出日志文件一看,恩,有问题了,所以本文产生了,也 ...