假设你是电信的实施工程师,需要为一个镇的九个村庄架设通信网络做设计,村
庄位置大致如图,其中 Vo~V8是村庄,之间连线的数字表示村与村间的可通达
的直线距离,比如Vo至V1就是10公里(个别如Vo与V6,V6与V8,V5与V7未测算距
离是因为有高山或湖泊,不予考虑)。你们领导要求你必须用最小的成本完成这次任
务。你说怎么办?

这个问题实质上找连通网的最小生成树。

最小生成树定义:一个连通图的生成树是一个极小的连通子图,它含有图中全部的顶点,但只有足以构成一棵树的n-1条边。我们把构造连通网的最小代价生成树。称为最小生成树

经典的有两种算法,普里姆算法和克鲁斯卡尔算法

一、普里姆(Prim算法)

1.1、核心思想

用一个待定的最小权值的数组来保存每一个将来有可能跟我们相连的邻接点的最小权值,通过将顶点连通的方式,不断的连接,直至找到最后的最小权值。

1.2、步骤模拟

1,就上图而言,首先顶点v0+顶点v1看做一个整体,保存最小权值10。

那么就有

v0: 0 10 # # # 11 # # #

v1: 10 0 18 # # # 16 # 12

sum: 0 0 18 # # 11 16 # 12

连通的记为0,都有数字的取最小值,都是不可连通#相加仍不可连通

2,然后在连通的边18、11、16、12中,11最小,加上顶点v5

3,依次重复。最小权值的数组变化过程如下图所示(其中#表示不可连通),

4,最后得到最小生成树10+11+12+8+16+19+7+16=99,也就是解决了上述施工问题。

v0 v1 v2 v3 v4 v5 v6 v7 v8       顶点变化
0  10  #  #  # 11  #  # #        v0
0  0  18  #  # 11  16 # 12       +v1:10
0  0  18  #  26 0  16 # 12       +v5:11
0  0  8  21  26 0  16 # 0            +v8:12
0  0  8  21  26 0  16 # 0      +v2:8
0  0  0  21  26 0  16 # 0      +v6:16
0  0  0  21  26 0  0 19 0      +v7:19
0  0  0  16  7  0  0  0 0      +v4:7
0  0  0  16  0  0  0  0 0      +v3:16
0  0  0  0   0  0  0  0 0

1.3、实现如下

public class Graph {private int vertexSize;//顶点数量private int[] vertexs;//顶点数组private int[][] matrix;private static final int MAX_WEIGHT = 1000;public Graph(int vertextSize) {this.vertexSize = vertextSize;matrix = new int[vertextSize][vertextSize];vertexs = new int[vertextSize];for (int i = 0; i < vertextSize; i++) {vertexs[i] = i;}}/*** prim 普里姆算法*/public void prim() {//最小代价顶点权值的数组,为0表示已经获取最小权值int[] lowcost = new int[vertexSize];//放顶点权值int[] adjvex = new int[vertexSize];int min, minIndex, sum = 0;// 拷贝v0到lowcost数组for (int i = 1; i < vertexSize; i++) {lowcost[i] = matrix[0][i];}// 从v1开始遍历for (int i = 1; i < vertexSize; i++) {min = MAX_WEIGHT;minIndex = 0;//找最小值、最小值indexfor (int j = 1; j < vertexSize; j++) {if (lowcost[j] < min && lowcost[j] > 0) {min = lowcost[j];minIndex = j;}}System.out.println("顶点:" + adjvex[minIndex] + ",权值:" + min);//加上每个阶段的最小生成树sum += min;//找到置0表示已经连通lowcost[minIndex] = 0;for (int j = 1; j < vertexSize; j++) {if (lowcost[j] != 0 && matrix[minIndex][j] < lowcost[j]) {// 顶点相加:lowcost加上matrix[minIndex]lowcost[j] = matrix[minIndex][j];// 记录是哪个顶点adjvex[j] = minIndex;}}}System.out.println("最小生成树权值和:" + sum);}public static void main(String[] args) {Graph graph = new Graph(9);int[] a1 = new int[]{0, 10, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 11, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT};int[] a2 = new int[]{10, 0, 18, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 16, MAX_WEIGHT, 12};int[] a3 = new int[]{MAX_WEIGHT, MAX_WEIGHT, 0, 22, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 8};int[] a4 = new int[]{MAX_WEIGHT, MAX_WEIGHT, 22, 0, 20, MAX_WEIGHT, MAX_WEIGHT, 16, 21};int[] a5 = new int[]{MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 20, 0, 26, MAX_WEIGHT, 7, MAX_WEIGHT};int[] a6 = new int[]{11, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 26, 0, 17, MAX_WEIGHT, MAX_WEIGHT};int[] a7 = new int[]{MAX_WEIGHT, 16, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 17, 0, 19, MAX_WEIGHT};int[] a8 = new int[]{MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 16, 7, MAX_WEIGHT, 19, 0, MAX_WEIGHT};int[] a9 = new int[]{MAX_WEIGHT, 12, 8, 21, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 0};graph.matrix[0] = a1;graph.matrix[1] = a2;graph.matrix[2] = a3;graph.matrix[3] = a4;graph.matrix[4] = a5;graph.matrix[5] = a6;graph.matrix[6] = a7;graph.matrix[7] = a8;graph.matrix[8] = a9;graph.prim();}
}

运行结果

顶点:0,权值:10
顶点:0,权值:11
顶点:1,权值:12
顶点:8,权值:8
顶点:1,权值:16
顶点:6,权值:19
顶点:7,权值:7
顶点:7,权值:16
最小生成树权值和:99

二、克鲁斯卡尔(Kruskal算法)

2.1、核心思想

以边为数据结构来构造图,按照边的权重从小到大排列,然后依次相加,判断如果回环则跳过这条边,直至找到最后的最小权值。

2.2、步骤模拟

1,首先我们以边来构造图得到右图表格,数据存储begin、end、weight

2,然后按顺序加起来,这里前7条边相加都不构成回环,到第8条(v5,v6),判断v5 -> v0 -> v1 -> v6已构成回环,所以跳过这条边。

3,依次重复。最小权值的边变化过程如下图所示

(v4,v7) +7
(v2,v8) +8
(v0,v1) +10
(v0,v5) +11
(v1,v8) +12
(v3,v7) +16
(v1,v6) +16
(v5,v6) 回环: v5 -> v0 -> v1 -> v6
(v1,v2) 回环: v1 -> v8 -> v2
(v6,v7) +19
(v3,v4) 回环: v3 -> v7 -> v4
(v3,v8) 回环: v3 -> v7 -> v6 -> v1 -> v8
(v2,v3) 回环: v2 -> v8 -> v3
(v3,v6) 回环: v3 -> v4 -> v7 -> v6
(v4,v5) 回环: v4 -> v3 -> v6 -> v5

4,最后得到最小生成树17+8+10+11+12+16+16+19=99,也就是解决了上述施工问题。

2.3、回环判断逻辑

1,构造长度为edgeSize的数组;

2,每次加入一条边就以begin为下标,end为数值存入数组;

如:edges(4,7)这条边 => [0,0,0,7,0] ,即 a[4]=7

3,同时通过判断新加入边的begin、end在数组中最终指向同一个值来判断回环

2.4、实现如下

// 克鲁斯卡尔算法
public class GraphKruskal {private Edge[] edges;private int edgeSize;public GraphKruskal(int edgeSize) {this.edgeSize = edgeSize;edges = new Edge[edgeSize];}public void miniSpanTreeKruskal() {int m, n, sum = 0;int[] parent = new int[edgeSize];//神奇的数组,下标为起点,值为终点for (int i = 0; i < edgeSize; i++) {parent[i] = 0;}for (int i = 0; i < edgeSize; i++) {n = find(parent, edges[i].begin);m = find(parent, edges[i].end);if (n != m) {parent[n] = m;System.out.println("边添加成功 -> (" + edges[i].begin + "," + edges[i].end + "): +" + edges[i].weight);sum += edges[i].weight;} else {System.out.println("第" + i + "条边回环了");}}System.out.println("sum:" + sum);}/** 将神奇数组进行查询获取非回环的值*/public int find(int[] parent, int f) {while (parent[f] > 0) {int temp = f;f = parent[f];System.out.println("找到路径:(" + temp + "," + f + ")");}return f;}public void createEdgeArray() {edges[0] = new Edge(4, 7, 7);edges[1] = new Edge(2, 8, 8);edges[2] = new Edge(0, 1, 10);edges[3] = new Edge(0, 5, 11);edges[4] = new Edge(1, 8, 12);edges[5] = new Edge(3, 7, 16);edges[6] = new Edge(1, 6, 16);edges[7] = new Edge(5, 6, 17);edges[8] = new Edge(1, 2, 18);edges[9] = new Edge(6, 7, 19);edges[10] = new Edge(3, 4, 20);edges[11] = new Edge(3, 8, 21);edges[12] = new Edge(2, 3, 22);edges[13] = new Edge(3, 6, 24);edges[14] = new Edge(4, 5, 26);}class Edge {private int begin;private int end;private int weight;public Edge(int begin, int end, int weight) {super();this.begin = begin;this.end = end;this.weight = weight;}}public static void main(String[] args) {GraphKruskal graphKruskal = new GraphKruskal(15);graphKruskal.createEdgeArray();graphKruskal.miniSpanTreeKruskal();}
}

运行结果:

边添加成功 -> (4,7): +7
边添加成功 -> (2,8): +8
边添加成功 -> (0,1): +10
找到路径:(0,1)
边添加成功 -> (0,5): +11
找到路径:(1,5)
边添加成功 -> (1,8): +12
边添加成功 -> (3,7): +16
找到路径:(1,5)
找到路径:(5,8)
边添加成功 -> (1,6): +16
找到路径:(5,8)
找到路径:(8,6)
第7条边回环了
找到路径:(1,5)
找到路径:(5,8)
找到路径:(8,6)
找到路径:(2,8)
找到路径:(8,6)
第8条边回环了
边添加成功 -> (6,7): +19
找到路径:(3,7)
找到路径:(4,7)
第10条边回环了
找到路径:(3,7)
找到路径:(8,6)
找到路径:(6,7)
第11条边回环了
找到路径:(2,8)
找到路径:(8,6)
找到路径:(6,7)
找到路径:(3,7)
第12条边回环了
找到路径:(3,7)
找到路径:(6,7)
第13条边回环了
找到路径:(4,7)
找到路径:(5,8)
找到路径:(8,6)
找到路径:(6,7)
第14条边回环了
sum:99

【数据结构】图的最小生成树算法相关推荐

  1. 数据结构(19)图的最小生成树算法

    数据结构(19)图的最小生成树算法 前言 普里姆(Prim)算法 克鲁斯卡尔(Kruskal)算法 代码 GraphMtx.h GraphMtx.c Main.c 前言 在有n个顶点的图中,要连接所有 ...

  2. 【数据结构】——图的最小生成树算法(普里姆+克鲁斯卡尔)

    这里的图指的是带权无向图,也就是无向网. 关于最小生成树 图的最小生成树要解决的问题:用最小的代价连通图中的所有顶点. 下面两种算法都是运用贪心思想,利用MST(Minimum Spanning Tr ...

  3. 数据结构—图(Part Ⅱ)—最小生成树 最短路径

    目录 最小生成树 普里姆(Prim)算法 算法实现 运行结果 程序分析 克鲁斯卡尔(Kruskal)算法 算法实现 运行结果 程序分析 最短路径 广度优先搜索(BFS)算法 算法实现 运行结果 程序分 ...

  4. 大话数据结构18:最小生成树算法

    prim最小生成树算法 对于几个图G{V,E};首先从V中任意选择一个顶点Vo 将其加入到顶点集合U中,在顶点集合V-U中计算所有到V中任意顶点假设是Vo最近的顶点Voo,将其加入到U中,并且记录边E ...

  5. 图的最小生成树算法实现(Prim + Kruskal)

    1.采用书上第 161 页定义的图的邻接矩阵存储表示,编程实现构造最小生成树的 Prim 算法. 2.采用书上第 161 页定义的图的邻接矩阵存储表示,编程实现构造最小生成树的 Kruskal 算法. ...

  6. 图的最小生成树算法(图解+代码)| 学不会来看我系列

    文章目录 最小生成树 Prim算法 1.介绍 2.图解步骤 3.算法分析 算法问题 解决方案 4.代码实现 Kruskal算法 1.介绍 2.图解 3.算法分析 算法问题 解决方案 4.代码实现 最小 ...

  7. 数据结构——图——最短路径DF算法

    一.Dijkstra算法(贪心地求最短距离的算法) 在此算法中,我按照自己的理解去命名,理解起来会轻松一些. #define MAXSIZE 100 #define UNVISITED 0 #defi ...

  8. 数据结构——图的定义和实现

    这里写目录标题 图的定义 各种图的定义 无向边 有向边 图的顶点与边的关系 连通图生成树 图的各种实现 在引入邻接矩阵之前先介绍一下图的相关概念(概念比前树啥的面稍微复杂一点) 图的定义 之前学了线性 ...

  9. 基于C++的带权无向图的实现 (三)- Prim最小生成树算法

    该系列文章是本人整理的有关带权无向图的数据结构和算法的分析与实现,若要查看源码可以访问我的github仓库,如有问题或者建议欢迎各位指出. 目录 基于C++的带权无向图的实现 (一)- 数据结构 基于 ...

最新文章

  1. 从ACT-R探讨认知智能
  2. setup.py安装
  3. 隔行换色案例||全选和全不选||QQ表情选择||多选下拉列表左右移动
  4. Arcface v1 论文翻译与解读
  5. 谷歌深度学习四大教训:应用、系统、数据及原理(附数据集列表)
  6. [JLOI2015]管道连接(斯坦纳树)
  7. windows 7 64bit python3.3安装pyqt
  8. 功成身退:AMD Mantle不再优化了
  9. mybatis如何防止sql注入
  10. 极简代码 —— list 最小最大索引(argmax/argmin)的实现
  11. 论文笔记(3):STC: A Simple to Complex Framework for Weakly-supervised Semantic Segmentation
  12. MySql 8 命令
  13. 基于python的图书管理系统设计与实现论文_图书馆管理系统的设计与实现毕业论文...
  14. excel之列联表分析
  15. Xcode 报错 ERROR ITMS-90096,启动图黑屏
  16. 小曾带你刷牛客03(Java版本)
  17. c语言计算个人成绩平均分,C语言 | 计算总平均分及第n个人的成绩
  18. 数据的写出(FileWriter)
  19. matlab接收电视信号,DRM接收及matlab实验 (转载)
  20. RFID与物联网的关系

热门文章

  1. hash redis springboot_SpringBoot 操作 Redis 详解
  2. Linux 系统运行级别
  3. Python爬虫笔记——if __name__ == ‘__main__‘ 如何正确理解和__init__和self 的解析
  4. IntelliJ IDEA插件安装最全详解
  5. 右边补0 润乾报表_润乾报表查询所有汇总
  6. 犀牛软件自定义保存截图
  7. 第十二课 从宠物商店案例看DAPP架构和WEB3.JS交互接口
  8. 场景编程集锦-月光族的期待
  9. 数据结构到底是个什么玩意儿?——数据结构总结篇
  10. spring MVC 获取servletContext,实现文件下载功能