首先我们获取这个图

根据这个图我们可以得到对应的二维矩阵图数据

根据kruskal算法的思想,首先提取所有的边,然后把所有的边进行排序

思路就是把这些边按照从小到大的顺序组装,至于如何组装

这里用到并查算法的思路

* 1、makeset(x),也就是生成单元素集合,也就是每一个节点
* 2、find(x) 返回一个包含x的子集,这个集合可以看成一个有根树
* 3、union(x,y) 构造分别包含x和y的不相交的子集子集Sx和Sy的并集,这里尤为关键:!!!!

了解到这些思路之后,开始我们的算法

第一步:获取这个文件的矩阵数据,存放到对象中

  1. package cn.xf.algorithm.ch09Greedy.vo;
  2.  
  3. import java.io.BufferedInputStream;
  4. import java.io.BufferedReader;
  5. import java.io.File;
  6. import java.io.FileInputStream;
  7. import java.io.FileNotFoundException;
  8. import java.io.IOException;
  9. import java.util.ArrayList;
  10. import java.util.List;
  11.  
  12. import org.junit.Test;
  13.  
  14. public class MGraph {
  15. private int eleSize;
  16. private int nums[][];
  17. private List<KruskalBianVo> kruskalBianVos = new ArrayList<KruskalBianVo>();
  18.  
  19. public MGraph() {
  20. // TODO Auto-generated constructor stub
  21. }
  22.  
  23. public MGraph(int eleSize, int[][] nums) {
  24. this.eleSize = eleSize;
  25. this.nums = nums;
  26. }
  27.  
  28. public MGraph(File file) throws Exception {
  29. if(file.exists()) {
  30. //读取数据流,获取数据源
  31. FileInputStream fis;
  32. BufferedInputStream bis;
  33. try {
  34. fis = new FileInputStream(file);
  35. //缓冲
  36. bis = new BufferedInputStream(fis);
  37. byte buffer[] = new byte[1024];
  38. while(bis.read(buffer) != -1) {
  39. String allData = new String(buffer);
  40. String lines[] = allData.split("\r\n");
  41. int allLines = lines.length;
  42. int allColumns = lines[0].split(" ").length;
  43. if(allLines < allColumns) {
  44. //如果行比较小
  45. eleSize = allLines;
  46. } else {
  47. //否则以列为准
  48. eleSize = allColumns;
  49. }
  50. nums = new int[eleSize][eleSize];
  51. for(int i = 0; i < eleSize; ++i) {
  52. //对每一行数据进行入库处理
  53. String everyNums[] = lines[i].split(" ");
  54. for(int j = 0; j < eleSize; ++j) {
  55. nums[i][j] = Integer.parseInt(everyNums[j]);
  56. }
  57. }
  58. }
  59.  
  60. //获取这个矩阵的所有边 kruskalBianVos
  61. for(int i = 0; i < eleSize; ++i) {
  62. for(int j = i + 1; j < eleSize; ++j) {
  63. if(nums[i][j] < 999) {
  64. KruskalBianVo kruskalBianVo = new KruskalBianVo();
  65. kruskalBianVo.setBeginNode(i);
  66. kruskalBianVo.setEndNode(j);
  67. kruskalBianVo.setLength(nums[i][j]);
  68. kruskalBianVos.add(kruskalBianVo);
  69. }
  70. }
  71. }
  72.  
  73. } catch (FileNotFoundException e) {
  74. e.printStackTrace();
  75. }
  76. } else {
  77. System.out.println("文件不存在");
  78. }
  79. }
  80.  
  81. public int getEleSize() {
  82. return eleSize;
  83. }
  84. public void setEleSize(int eleSize) {
  85. this.eleSize = eleSize;
  86. }
  87. public int[][] getNums() {
  88. return nums;
  89. }
  90. public void setNums(int[][] nums) {
  91. this.nums = nums;
  92. }
  93.  
  94. public List<KruskalBianVo> getKruskalBianVos() {
  95. return kruskalBianVos;
  96. }
  97.  
  98. public void setKruskalBianVos(List<KruskalBianVo> kruskalBianVos) {
  99. this.kruskalBianVos = kruskalBianVos;
  100. }
  101.  
  102. public static void main(String[] args) {
  103. String path = MGraph.class.getResource("").getPath();
  104. path = path.substring(0, path.indexOf("/vo"));
  105. File f = new File(path + "/resource/test.txt");
  106. try {
  107. MGraph mg = new MGraph(f);
  108. System.out.println(mg.getKruskalBianVos().size());
  109. int rr[][] = mg.getNums();
  110. System.out.println(rr);
  111. } catch (Exception e) {
  112. e.printStackTrace();
  113. }
  114. }
  115.  
  116. }

  

数据对象:

第二步:建立相应的复制类:

存放边数据vo

  1. package cn.xf.algorithm.ch09Greedy.vo;
  2.  
  3. public class KruskalBianVo {
  4.  
  5. private int beginNode; //开始节点的index
  6. private int endNode; //结束节点的index
  7. private int length; //边长
  8. public int getBeginNode() {
  9. return beginNode;
  10. }
  11. public void setBeginNode(int beginNode) {
  12. this.beginNode = beginNode;
  13. }
  14. public int getEndNode() {
  15. return endNode;
  16. }
  17. public void setEndNode(int endNode) {
  18. this.endNode = endNode;
  19. }
  20. public int getLength() {
  21. return length;
  22. }
  23. public void setLength(int length) {
  24. this.length = length;
  25. }
  26.  
  27. }

  

交换辅助类

  1. package cn.xf.algorithm.ch09Greedy.util;
  2.  
  3. import java.util.List;
  4.  
  5. import cn.xf.algorithm.ch09Greedy.vo.KruskalBianVo;
  6.  
  7. public class Greedy {
  8.  
  9. public static void swapKruskalBianVo(List<KruskalBianVo> kruskalBianVo, int left, int right) {
  10. if(kruskalBianVo == null || kruskalBianVo.size() <= 1 || left >= right) {
  11. return;
  12. }
  13.  
  14. //交换节点
  15. KruskalBianVo kruskalBianVoTemp = kruskalBianVo.get(left);
  16. kruskalBianVo.set(left, kruskalBianVo.get(right));
  17. kruskalBianVo.set(right, kruskalBianVoTemp);
  18. }
  19. }

  

对边进行快排辅助类

  1. package cn.xf.algorithm.ch09Greedy.util;
  2.  
  3. import java.util.List;
  4.  
  5. import cn.xf.algorithm.ch09Greedy.vo.KruskalBianVo;
  6.  
  7. public class QuikSort {
  8. //先找中间点
  9. public static int getMiddlePoint(List<KruskalBianVo> kruskalBianVo, int left, int right, boolean isMinToMax) {
  10. if(kruskalBianVo == null || kruskalBianVo.size() <= 1 || left >= right) {
  11. return left;
  12. }
  13. //开始快排核心程序,就是对数列两边进行交换
  14. //1、首选第一个元素作为第一个参照元素
  15. //2、设置左边向右遍历的起点,设定右边向左遍历的起点
  16. KruskalBianVo midValue = kruskalBianVo.get(left);
  17. int leftIndex = left + 1;
  18. int rightIndex = right;
  19. int count = 0;
  20. //循环遍历,知道left跑到right的右边
  21. while(leftIndex < rightIndex) {
  22. //确定好区间之后交换位置
  23. if(isMinToMax) {
  24. //从小到大
  25. //遍历左边数据
  26. while(kruskalBianVo.get(leftIndex).getLength() <= midValue.getLength() && leftIndex < right) {
  27. ++leftIndex;
  28. }
  29. //遍历右边数据
  30. while(kruskalBianVo.get(rightIndex).getLength() > midValue.getLength() && rightIndex > left) {
  31. --rightIndex;
  32. }
  33. } else {
  34. //如果是从大到小
  35. //遍历左边数据
  36. while(kruskalBianVo.get(leftIndex).getLength() > midValue.getLength()) {
  37. ++leftIndex;
  38. }
  39. //遍历右边数据
  40. while(kruskalBianVo.get(rightIndex).getLength() < midValue.getLength()) {
  41. --rightIndex;
  42. }
  43. }
  44. //交换位置
  45. Greedy.swapKruskalBianVo(kruskalBianVo, leftIndex, rightIndex);
  46. ++count;
  47. }
  48. //最后一次交换之后是不必要的交换,因为已经错开位置了,这里做一个调整
  49. //交换位置
  50. if(count > 0) {
  51. //如果进入过循环,那么肯定进行了一次,交换,那么要撤销那一次的无效
  52. Greedy.swapKruskalBianVo(kruskalBianVo, leftIndex, rightIndex);
  53. //吧最开始的位置和中间的位置进行交换
  54. //交换位置
  55. Greedy.swapKruskalBianVo(kruskalBianVo, left, rightIndex);
  56. }
  57.  
  58. //返回中间位置的索引
  59. return rightIndex;
  60. }
  61.  
  62. public static void sort(List<KruskalBianVo> kruskalBianVo, Boolean isMinToMax) {
  63. if(kruskalBianVo == null || kruskalBianVo.size() <= 0)
  64. return;
  65. if(isMinToMax == null)
  66. isMinToMax = true;
  67. sort(kruskalBianVo, 0, kruskalBianVo.size() - 1, isMinToMax);
  68. }
  69.  
  70. private static void sort(List<KruskalBianVo> kruskalBianVo, int left, int right, Boolean isMinToMax) {
  71. if(left < right) {
  72. //如果左索引小于右索引,那么执行递归
  73. int mid = getMiddlePoint(kruskalBianVo, left, right, isMinToMax);
  74. sort(kruskalBianVo, left, mid - 1, isMinToMax);
  75. sort(kruskalBianVo, mid + 1, right, isMinToMax);
  76. }
  77. }
  78. }

  

存放树节点的tree结构

  1. package cn.xf.algorithm.tree;
  2.  
  3. import java.util.List;
  4.  
  5. /**
  6. * 树节点
  7. *
  8. * .
  9. *
  10. * @author xiaof
  11. * @version Revision 1.0.0
  12. * @see:
  13. * @创建日期:2017年8月18日
  14. * @功能说明:
  15. *
  16. */
  17. public class TreeNode {
  18. private List<TreeNode> nextNodes;
  19. private Object value;
  20. private TreeNode parent = null; //指向父节点
  21. public TreeNode() {
  22. }
  23. public TreeNode(List<TreeNode> nextNodes, Object value) {
  24. this.nextNodes = nextNodes;
  25. this.value = value;
  26. }
  27. public List<TreeNode> getNextNodes() {
  28. return nextNodes;
  29. }
  30. public void setNextNodes(List<TreeNode> nextNodes) {
  31. this.nextNodes = nextNodes;
  32. }
  33. public Object getValue() {
  34. return value;
  35. }
  36. public void setValue(Object value) {
  37. this.value = value;
  38. }
  39. public TreeNode getParent() {
  40. return parent;
  41. }
  42. public void setParent(TreeNode parent) {
  43. this.parent = parent;
  44. }
  45. }

  

最后实现kruskal算法的核心程序

各个节点选中过程图展示

  1. package cn.xf.algorithm.ch09Greedy;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. import cn.xf.algorithm.ch09Greedy.util.QuikSort;
  7. import cn.xf.algorithm.ch09Greedy.vo.KruskalBianVo;
  8. import cn.xf.algorithm.ch09Greedy.vo.MGraph;
  9. import cn.xf.algorithm.tree.TreeNode;
  10.  
  11. /**
  12. * kruskal 算法, 寻找最小生成树
  13. * 功能: http://blog.csdn.net/luomingjun12315/article/details/47700237\
  14. * http://blog.csdn.net/niushuai666/article/details/6689285
  15. * http://blog.sina.com.cn/s/blog_a00f56270101a7op.html
  16. * @author xiaofeng
  17. * @date 2017年8月21日
  18. * @fileName KruskalAlgorithm.java
  19. *
  20. * 判定回环:判定回环的思路是,如果两个节点联通之后,存在回环,那么两个节点往上遍历这颗树,最终肯定会汇集到根节点,
  21. * 如果两个节点相连的这根线不存在回环中,那么往上遍历节点,两个节点的根就不会重逢
  22. * 那么这里有个点
  23. * 1。这根节点的上级节点存储问题
  24. * 这里采用数组,也就是V[I]标识I的父节点,这样来存储,当没有改变的时候,也就是没有上级的时候,那么根节点就是本身
  25. *
  26. * 2。如何添加节点的父节点
  27. * 如果这个节点的被修改过了,存在上级节点,那么就把这个a起点作为起点,b作为后续节点
  28. * 否则,以另一个作为起点
  29. *
  30. * 对于是否回环的问题,可以看看,不相交子集和并查算法
  31. * 其中并查算法:快速求并的思路,分三步实现
  32. * 1、makeset(x),也就是生成单元素集合,也就是每一个节点
  33. * 2、find(x) 返回一个包含x的子集,这个集合可以看成一个有根树
  34. * 3、union(x,y) 构造分别包含x和y的不相交的子集子集Sx和Sy的并集,这里尤为关键:!!!!
  35. * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  36. * ! 这里快速求并的核心思路是, !!
  37. * ! 吧Sy树的根附加到Sx的根上,也就是新子集的根作为X树的根的一个孩子节点附加进入 !!
  38. * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  39. */
  40. public class KruskalAlgorithm {
  41.  
  42. public void kruska(MGraph mg) {
  43. if(mg == null)
  44. return;
  45. //获取图的所有边信息
  46. List<KruskalBianVo> allBian = mg.getKruskalBianVos();
  47. //进行排序处理,这里用一个快排
  48. QuikSort.sort(allBian, true);
  49. //排序结束之后按照从小到大的顺序进行操作
  50. int ecounter = 0;
  51. //为求是否有回环,使用快速求并的方式构造不同的子树
  52. //1、 makeset 阶段,建立容器,存放森林
  53. List<TreeNode> allTree = new ArrayList<TreeNode>();
  54. for(int i = 0; i < mg.getEleSize(); ++i) {
  55. TreeNode treeNode = new TreeNode();
  56. treeNode.setValue(i); //第i个节点
  57. allTree.add(treeNode);
  58. }
  59. //根据节点个数,按照A-Z进行设置名字
  60. char names[] = new char[mg.getEleSize()];
  61. for(int i = 0; i < mg.getEleSize(); ++i) {
  62. names[i] = (char) ('A' + i);
  63. }
  64.  
  65. //2、find(x) 寻找x节点加入集合中
  66. int k = 0;
  67. while(ecounter < mg.getEleSize() && k < allBian.size()) {
  68. //获取节点对象
  69. KruskalBianVo kruskalBianVo = allBian.get(k);
  70. //判断当前边的两个节点加入之后是否有回路
  71. int first = kruskalBianVo.getBeginNode();
  72. int second = kruskalBianVo.getEndNode();
  73. //求并 3、union(x,y)
  74. if(union(allTree, first, second)) {
  75. //如果顺利加入
  76. System.out.println("[" + names[first] + "]=>[" + names[second] + "] 边长为:" + kruskalBianVo.getLength());
  77. ++ecounter;
  78. }
  79. ++k; // 计数循环
  80. }
  81.  
  82. }
  83.  
  84. /**
  85. * 判断能否合并的,就是寻找根部父节点是否一致
  86. * @param allTree
  87. * @param first
  88. * @param second
  89. * @return
  90. */
  91. public Boolean union(List<TreeNode> allTree, int first, int second) {
  92. TreeNode root1 = getRoot(allTree.get(first));
  93. TreeNode root2 = getRoot(allTree.get(second));
  94. if(root1 == root2) {
  95. return false;
  96. } else {
  97. //如果不同根,那么把一边的根加入到一边中
  98. root2.setParent(root1);
  99. }
  100.  
  101. return true;
  102. }
  103.  
  104. private TreeNode getRoot(TreeNode current) {
  105. if(current.getParent() == null)
  106. return current;
  107. else {
  108. return getRoot(current.getParent());
  109. }
  110. }
  111.  
  112. /**
  113. * 孩子节点是否包含
  114. * @param root
  115. * @param value
  116. * @return
  117. */
  118. public Boolean haveChild(TreeNode root, Object value) {
  119. //判断颗树是否有对应的孩子节点
  120. Boolean result = false;
  121. if(root.getValue().equals(value)) {
  122. return true;
  123. } else {
  124. for(int i = 0; i < root.getNextNodes().size(); ++i) {
  125. //判断孩子节点的孩子。。。是否包含
  126. if(haveChild(root.getNextNodes().get(i), value)) {
  127. result = true;
  128. break;
  129. }
  130. }
  131. }
  132. return result;
  133. }
  134.  
  135. public Boolean findParent(TreeNode root, Object value) {
  136. //判断颗树是否有对应的孩子节点
  137. Boolean result = false;
  138. if(root.getParent() != null) {
  139. //存在父节点
  140. if(root.getParent().getValue().equals(value)) {
  141. result = true;
  142. } else {
  143. result = findParent(root.getParent(), value);
  144. }
  145. }
  146.  
  147. return result;
  148. }
  149.  
  150. }

  

测试结果:

  1. package algorithm.ch09Greedy;
  2.  
  3. import java.io.File;
  4.  
  5. import org.junit.Test;
  6.  
  7. import cn.xf.algorithm.ch09Greedy.KruskalAlgorithm;
  8. import cn.xf.algorithm.ch09Greedy.vo.MGraph;
  9.  
  10. public class KruskalTest {
  11.  
  12. @Test
  13. public void test() {
  14. String path = KruskalTest.class.getResource("").getPath();
  15. // System.out.println(path);
  16. File inputFile = new File(path + "/test.txt");
  17. MGraph mg = null;
  18. try {
  19. mg = new MGraph(inputFile);
  20. } catch (Exception e) {
  21. e.printStackTrace();
  22. }
  23.  
  24. KruskalAlgorithm kruskalAlgorithm = new KruskalAlgorithm();
  25.  
  26. kruskalAlgorithm.kruska(mg);
  27.  
  28. }
  29.  
  30. @Test
  31. public void test2() {
  32. System.out.println('A' - 1);
  33. }
  34. }

  

展示:

包结构:

【算法设计与分析基础】24、kruskal算法详解的更多相关文章

  1. 算法设计与分析基础 (Anany Levitin 著)

    第1章 绪论 1.1 什么是算法 1.2 算法问题求解基础 1.2.1 理解问题 1.2.2 了解计算设备的性能 1.2.3 在精确解法和近似解法之间做出选择 1.2.4 算法的设计技术 1.2.5 ...

  2. 【算法设计与分析基础】25、单起点最短路径的dijkstra算法

    首先看看这换个数据图 邻接矩阵 dijkstra算法的寻找最短路径的核心就是对于这个节点的数据结构的设计 1.节点中保存有已经加入最短路径的集合中到当前节点的最短路径的节点 2.从起点经过或者不经过 ...

  3. 算法设计与分析 - AC 题目 - 第 2 弹

    PTA-算法设计与分析-AC原题7-1 最大子列和问题 (20分)给定K个整数组成的序列{ N1, N2, ..., NK },“连续子列”被定义为{ Ni, Ni+1, ..., Nj },其中 1 ...

  4. 【技术文档】《算法设计与分析导论》R.C.T.Lee等·第7章 动态规划

    由于种种原因(看这一章间隔的时间太长,弄不清动态规划.分治.递归是什么关系),导致这章内容看了三遍才基本看懂动态规划是什么.动态规划适合解决可分阶段的组合优化问题,但它又不同于贪心算法,动态规划所解决 ...

  5. 算法设计与分析 - AC 题目 - 第 5 弹(重复第 2 弹)

    PTA-算法设计与分析-AC原题 - 最大子列和问题 (20分) 给定K个整数组成的序列{ N1, N2, ..., NK },“连续子列”被定义为{ Ni, Ni+, ..., Nj },其中 ≤i ...

  6. 算法设计与分析-Week12

    题目描述 You are given coins of different denominations and a total amount of money amount. Write a func ...

  7. pyhanlp 共性分析与短语提取内容详解

    pyhanlp 共性分析与短语提取内容详解   简介 HanLP中的词语提取是基于互信息与信息熵.想要计算互信息与信息熵有限要做的是 文本分词进行共性分析.在作者的原文中,有几个问题,为了便于说明,这 ...

  8. ELK&ElasticSearch5.1基础概念及配置文件详解【转】

    1. 配置文件 elasticsearch/elasticsearch.yml 主配置文件 elasticsearch/jvm.options jvm参数配置文件 elasticsearch/log4 ...

  9. 计算机网络基础之IP地址详解

    计算机网络基础之IP地址详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.IP地址概述 1>.什么是IP地址 我们为什么要使用逻辑地址(IP地址)来标识网络设备,而不采 ...

随机推荐

  1. shell,bash,zsh,console,terminal到底是什么意思,它们之间又是什么关系?

    原文链接 终端(terminal,或者叫物理终端):是一种设备,不是一个程序,一般说的就是能提供命令行用户界面的设备,典型的是屏幕和键盘,或其他的一些物理终端.虚拟终端:屏幕和键盘只是一个终端,可能不 ...

  2. 计算出前N项的数据

    #include<iostream> #include<algorithm> #include<numeric> using namespace std; ; in ...

  3. 详解spl_autoload_register()函数

    一.__autoload 这是一个自动加载函数,在PHP5中,当我们实例化一个未定义的类时,就会触发此函数.看下面例子: printit.class.php    <?php    class  ...

  4. 移动GIS在企业各个行业中的应用解决方案

    “移动GIS的设备厂商越来越多地关注行业用户的需求,所以移动GIS的市场前景是非常广阔的.当前国内移动GIS,已广泛应用于测绘.国土.环境.水利.农业.林业和矿产等传统资源管理领域和城市规划方面.在应 ...

  5. windows10版本1709 在桌面和文件中点击右键,会引起卡顿

    windows自动升级到1709版本后出现的问题,而之前是没有这种问题的. 最终解决办法:(需要设置注册表) 运行:快捷键Win+R regedit 路径:可直接复制后粘贴+回车 HKEY_CLASS ...

  6. 电脑创建WIFI/无线热点之后, 手机QQ能上浏览器不能上网

    这个完全是个人经验,绝对原创,请尊重博主原创权,转载请注明转于此博客. 问题如题,大家电脑创建无线热点之后, 有的人手机会出现QQ,微信能上网, 但是浏览器或者基于浏览器的那些比如应用商店不能上网, ...

  7. 独立安装WAMP

    安装apache 获得apache安装软件: 建议去官网下载: www.apache.org 双击执行: 进入欢迎界面 点击"next"进入到协议界面 接收协议点击"ne ...

  8. C#基础知识 结构与类的区别

    网上看到struct与class之间的区别,都写的很多,当然说的是对的,也很详细.不过我个人不喜欢照本宣科,还是要有自己的理解和认识,方便记忆. (前提:对于值类型与引用类型有一定的认识) 结构最重要 ...

  9. 从Trie树到双数组Trie树

    Trie树 原理 又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种.它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,能在常数时间O(len)内实现插入和查 ...

  10. python文件操作及os模块常用命令

    1.文件打开 文件句柄 = open('文件路径', '模式') 2.文件操作 打开文件时,需要指定文件路径和以何等方式打开文件,打开后,即可获取该文件句柄,日后通过此文件句柄对该文件操作. 三种基本 ...