克鲁斯卡尔算法

问题描述

修路问题:七个村庄(A,B,C,D,E,F,G)要修路,七个村庄之间路线很多,要怎样才可以在保证联通所有村庄的同时修最短的路

思路分析

我们可以把所有的边存入数组,然后按照从小到大的顺序取出2构建最小树,但是要保证取出的边不会构成回路。如图我们有十条边,先都存入数组,然后取出依次取出,先取出最短的F->E,不构成回路,继续取C->D,依然图中不构成回路,继续取D->E,现在可以看见F->C这条线是联通的了,然后继续取最短,这时可以发现C->F是最短的,但是很明显的看到如果加入了C->F这条路就构成了回路。那我们是不是可以理解为按照从小到大的顺序去取出,只要不构成回路问题就解决了。

回路问题思路(重点理解)

如果我们有个数组动态记录每次取出后这些点的终点,那我们是不是就可以解决问题。最开始的时候每个点的终点就是自己,当第一次取出(F->E)这条边后,F的终点还是自己,但是E的终点就是F了,然后继续取,取出C->D,C的终点就是D,D的终点还是D,继续取D->E,当这条边取出来的时候D的终点就是跟E一样了(D的终点引用E的,E是F) ,因为已经串起来成为一条线了,那么此时(C,D,E,F)的终点都是F,继续取C->F,此时这条边加入的时候C指向F,F指向F。这时就构成回路了。简而言之,我们加入的边的两个顶点不能都指向同一个终点,否则将构成回路。

问题解决思路

这个时候我们要做的就是两件事。首先把所有的边存起来,然后排序,保证从小到大的顺序。第二步就是取出来每条边的同时判断是否构成回路。当边全部取完时,问题解决。

代码实现

public class Kruskal {private int edgeNum;//边的个数
private char[] vertexs;//顶点数组
private int[][] matrix; // 邻接矩阵
private static final int INF = Integer.MAX_VALUE;public static void main(String[] args) {char[] vertexs = {'A', 'B', 'C', 'D', 'E'
, 'F', 'G'};int[][] matrix = {/*A*//*B*//*C*//*D*//*E*//*F*//*G*//*A*/ {0, 12, INF, INF, INF, 16, 14},/*B*/ {12, 0, 10, INF, INF, 7, INF},/*C*/ {INF, 10, 0, 3, 5, 6, INF},/*D*/ {INF, INF, 3, 0, 4, INF, INF},/*E*/ {INF, INF, 5, 4, 0, 2, 8},/*F*/ {16, 7, 6, INF, 2, 0, 9},/*G*/ {14, INF, INF, INF, 8, 9, 0}};
Kruskal kruskal = new Kruskal(vertexs, matrix);
kruskal.print();kruskal.kruskal();}/**
* 定义构造方法初始化数据
* @param vertexs 顶点数组
* @param matrix  领接矩阵
*/
public Kruskal(char[] vertexs, int[][] matrix) {int vlen = vertexs.length;this.vertexs = new char[vlen];this.matrix = new int[vlen][vlen];// 初始化顶点for (int i = 0; i < vlen; i++) {this.vertexs[i] = vertexs[i];}// 初始化二维数组for (int i = 0; i < vlen; i++) {for (int j = 0; j < vlen; j++) {this.matrix[i][j] = matrix[i][j];}}// 计算有多少条边for (int i = 0; i < vlen; i++) {for (int j = i + 1; j < vlen; j++) {if (this.matrix[i][j] != INF) {edgeNum++;}}}
}public void kruskal(){// 结果数组的索引int index = 0;// 用于保存已生成的最小数中的每个顶点在最小生成树中的终点int[] ends = new int[edgeNum];// 创建结果数组 保存最后的最小生成树EData[] rets = new EData[edgeNum];// 获取图中所有的边集合EData[] edges = getEdges();sortEdges(edges);// 遍历数组将边添加到最小生成树是 判断是否形成回路for (int i = 0; i < edgeNum; i++) {int p1 = getPosition(edges[i].start);int p2 = getPosition(edges[i].end);// 获取p1顶点在这个最小生成树中的终点int m = getEnd(ends,p1);int n = getEnd(ends,p2);// 判断是否构成回路if (m != n){ends[m] = n; // 设置m在已有最小生成树中的终点rets[index++] = edges[i];}}System.out.println("最小生成树");for (int i = 0; i < index; i++) {System.out.println(rets[i]);}
}public void print() {System.out.println("领接矩阵为");for (int i = 0; i < vertexs.length; i++) {for (int j = 0; j < vertexs.length; j++) {System.out.printf("%13d", matrix[i][j]);}System.out.println();}
}// 我们需要对边的权值进行排序
private void sortEdges(EData[] edges) {for (int i = 0; i < edges.length - 1; i++) {for (int j = 0; j < edges.length - 1 - i; j++) {if (edges[j].weight > edges[j + 1].weight) {EData temp = edges[j];edges[j] = edges[j + 1];edges[j + 1] = temp;}}}
}// 返回顶点对应的下标
private int getPosition(char ch) {for (int i = 0; i < vertexs.length; i++) {if (vertexs[i] == ch) {return i;}}return -1;
}// 获取图中的边 放入数组中
private EData[] getEdges() {int index = 0;EData[] edgs = new EData[edgeNum];for (int i = 0; i < vertexs.length; i++) {for (int j = i + 1; j < vertexs.length; j++) {if (matrix[i][j] != INF) {edgs[index++] = new EData(vertexs[i], vertexs[j], matrix[i][j]);}}}return edgs;
}/***     获取下标为i的顶点的终点 用于后面判断两个顶点的* 终点是否相同* @param ends  数组就是记录各个顶点对应的终点是哪个*  ends数组是在遍历的过程中 逐步形成的* @param i  传入顶点的下标* @return 返回的是下标为i的这个顶点对应终点的下标*/private int getEnd(int[] ends,int i){while (ends[i] != 0){i = ends[i];}return i;}}/*** 创建一个类EData,它的对象实例就表示一条边*/
class EData {char start;char end;int weight;public EData(char start, char end, int weight) {this.start = start;this.end = end;this.weight = weight;}@Overridepublic String toString() {return "EData{<" +start +", " + end +">=" + weight +'}';}
}

运行结果

总结

相比起普利姆算法点击传送,克鲁斯卡尔相对适合求边稀疏的网的最小生成树。在这里,克鲁斯卡尔算法更多的是去考虑是否构成回路这个问题,如果这里想通了,应该就很容易想通了。

秃头萌新一枚 多多关照

克鲁斯卡尔算法_修路问题相关推荐

  1. JAVA-数据结构与算法-修路问题(普里姆算法)和公交站问题(克鲁斯卡尔算法)

    修路问题(普里姆算法) 最小生成树,给定一个带权的无向连通图,如何选择一颗生成树,使树上所有边上权的总和为最小:N个顶点,N-1条边 普里姆算法,在包含n个顶点的连通图中,找出只有n-1条边,包含所有 ...

  2. prim算法求最小生成树_克鲁斯卡尔算法(Kruskal算法)求最小生成树

    上一节介绍了求最小生成树之普里姆算法.该算法从顶点的角度为出发点,时间复杂度为O(n2),更适合与解决边的绸密度更高的连通网.本节所介绍的克鲁斯卡尔算法,从边的角度求网的最小生成树,时间复杂度为O(e ...

  3. 【算法】克鲁斯卡尔算法

    1.概述 视频地址:克鲁斯卡尔算法 下面看一个场景和问题 某城市新增7个站 点(A,B,C,D,E,F,G),现在需要修路把7个站点连通 各个站点的距离用边线表示(权),比如A-B距离12公里 问:如 ...

  4. 最小生成树-普利姆和克鲁斯卡尔算法

    目录 最小生成树 普利姆算法 算法介绍 代码 克鲁斯卡尔算法 算法介绍 步骤解析 回路 代码实现 最小生成树主要是用于解决修路问题等类似问题,要将所有顶点连通,并且权值之和最小. 最小生成树 给定一个 ...

  5. 并查集的一些个人观点 以及克鲁斯卡尔算法的详解

    先抛出个问题,什么是并查集,它有什么用? 看我这篇Blog的人想必就为了弄明白,下面写出我个人的一些观点. 1 什么是并查集,以及并查集要完成的目标. 举个例子,通火车要修路,已经修了一部分了,但各个 ...

  6. KruskalAlgorithm(克鲁斯卡尔算法)

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

  7. 克鲁斯卡尔算法的基本介绍和实现方法(Java)

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

  8. 克鲁斯卡尔算法解决公交站问题

    一 问题提出 1 某城市新增7个站点(A, B, C, D, E, F, G) ,现在需要修路把7个站点连通. 2 各个站点的距离用边线表示(权) ,比如 A – B 距离 12公里. 问:如何修路保 ...

  9. 克鲁斯卡尔算法(Kruskal)详解

    应用场景-公交站问题 看一个应用场景和问题: 1)某城市新增7个站点(A, B, C, D, E, F, G) ,现在需要修路把7个站点连通 2)各个站点的距离用边线表示(权) ,比如 A – B 距 ...

最新文章

  1. ImageView宽度填满屏幕,高度自适应
  2. 一分钟学会使用RichEdit控件
  3. Linux中后台执行scp
  4. 2017百度之星复赛:1003. Pokémon GO(递推)
  5. [转载] python---python中时间的应用(time模块)
  6. 【COCOS2DX-LUA 脚本开发之十二】利用AssetsManager实现在线更新资源文件
  7. 基于ESP32制作流光溢彩氛围灯
  8. CAD学习笔记中级课【坐标】
  9. 源代码转换成图-控制流图、数据流图
  10. 完美起点更重要:青立方超融合易捷版 助力企业一步云就绪
  11. 常规创业公司股权分配参考
  12. Java8新特性(一)—————Lambda表达式
  13. 【表单】如何去掉input被点击选中时的黑色边框
  14. 记一次神奇的CVPR 2021 Rebuttal 经历
  15. Nagios Core 复现
  16. v-model的基本使用
  17. 一些好的网页设计工具
  18. 光遇脚本弹琴_光遇自动弹琴脚本手机版
  19. 通过这一篇文章就了解机器学习的主要内容和核心思想(包括一些算法思想总结)!!!
  20. VS中让console程序不显示(隐藏)命令行窗口

热门文章

  1. 【笔试】羽毛球场地预定问题
  2. PPM、PCM和PWM的区别, I2S与pcm的区别
  3. 【AR-1】安装Unity+注册Vuforia,以及在Unity中导入Vuforia
  4. 通过FAR计算fRR
  5. 拨乱反正:MyISAM中key_buffer_size的设置
  6. 边缘设备、系统及计算杂谈(8)——dapr学习之一
  7. Chap和pap认证
  8. 天融信上网行为管理系统设置wifi短信验证流程
  9. 1.二分排序——抓老鼠法
  10. c++语言 幂指数,C++ pow(指数函数):求x的y次幂的值