Java 数据结构和经典算法经验总结

来源:互联网 发布:千牛卖家第三方软件 编辑:程序博客网 时间:2024/06/05 02:00

前言:在计算机软件专业中,算法分析与设计是一门非常重要的课程,很多人为它如痴如醉。很多问题的解决,程序的编写都要依赖它,在软件还是面向过程的阶段,就有‘程序=算法+数据结构’这个公式。算法的学习对于培养一个人的逻辑思维能力是有极大帮助的,它可以培养 我们养成思考分析问题,解决问题的能力。    如果一个算法有缺陷,或不适合某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂性和时间复杂度来衡量。算法可以使用自然语言、伪代码、流程图等多种不同的方法来描述。计算机系统中的操作系统、语言编译系统、数据库管理系统以及各种各样的计算机应用系统中的软件,都必须使用具体的算法来实现。算法设计与分析是计算机科学与技术的一个核心问题。因此,学习算法无疑会增强自己的竞争力,提高自己的修为,为自己增彩。

概念:算法简单来说就是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,也就是说算法告诉计算机怎么做,以此来解决问题。同一个问题存在多种算法来解决它,但是这些算法存在着优劣之分,好的算法速度快,效率高,占用空间小,差的算法不仅复杂难懂,而且效率低,对机器要求还高,当然,有时候算法之间存在一种互补关系,有些算法效率高,节省时间,但浪费空间,另外一些算法可能速度上慢些,但是空间比较节约,这时候 我们就应该根据实际要求,和具体情况来采取相应的算法来解决问题。

一: 

约瑟夫算法

约瑟夫环:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。

[java] view plain co
  1. public class YuesefuTest {  
  2.       
  3.     public static void main(String[] args) {  
  4.         int totalNum = 10;  
  5.         int countNum = 3;  
  6.           
  7.         yuesefuByMyself(totalNum, countNum);  
  8.         yuesefu(totalNum, countNum);  
  9.     }  
  10.       
  11.     /** 
  12.      * 此方法 k 为 list 的下标 
  13.      * @param totalNum 
  14.      * @param countNum 
  15.      */  
  16.     public static void yuesefuByMyself( int totalNum,int countNum){  
  17. //      初始化人数  
  18.         List<Integer> start = new ArrayList<Integer>();  
  19.           
  20.         for(int i=1; i<=totalNum; i++){  
  21.             start.add(i);  
  22.         }  
  23.           
  24. //      此处的k为list的下标,开始报数人的下标,第一个人为0,第n个人为n-1  
  25.         int k = 0;  
  26.         while(start.size()>0){  
  27. //          下一个出列人的下标,因为是从当前报数人开始数,所以减1为下一个出列人的下标  
  28.             k = k + countNum -1;  
  29.               
  30. //          当 下标+1 超过了 list 的size  
  31.             if(k + 1>start.size()){  
  32. //              当size 为 10 ,下标要取 10 ,最大下标为9,应该取 list 的 第一个,即下标为0,  
  33. //              同理,直接取余即可为正确下标  
  34.                 k = k % start.size();  
  35.             }  
  36.               
  37.             System.out.print(start.get(k)+",");  
  38.               
  39. //          出列,下一个开始报数人的下标即为出列人的下标  
  40.             start.remove(k);  
  41.         }  
  42.           
  43.         System.out.println();  
  44.     }  
  45.       
  46.     public static void yuesefu(int totalNum,int countNum){  
  47. //      初始化人数  
  48.         List<Integer> start = new ArrayList<Integer>();  
  49.           
  50.         for(int i=1; i<=totalNum; i++){  
  51.             start.add(i);  
  52.         }  
  53.           
  54. //      从第K个开始计数  
  55.         int k = 0;  
  56.         while(start.size()>0){  
  57.             k = k + countNum;  
  58. //          第m人的索引位置  
  59.             k = k % (start.size()) -1;  
  60. //          判断是否到队尾  
  61.             if(k<0){  
  62.                 System.out.print(start.get(start.size()-1)+",");  
  63.                 start.remove(start.size()-1);  
  64.                 k = 0;  
  65.             } else {  
  66.                 System.out.print(start.get(k)+",");  
  67.                 start.remove(k);  
  68.             }  
  69.         }  
  70.           
  71.         System.out.println();  
  72.     }  
  73.       
  74.   
  75. }  


二: 

二叉树的构建及遍历 

有一些博客构建出来的二叉树是完全二叉树,这篇博客什么样的二叉树都能构建,图为代码所示的一棵二叉树


public class BinaryTree {            private static String [] array = {"A","B","D","H","","","I","","","E","","J","","",              "C","F","","K","","","G","",""};      private static int arrayIndex = 0;        //  创建一棵二叉树,约定用户遵照前序遍历的方式输入数据  //  不使用迭代是因为迭代必须要知道这棵树有多深,  //  递归只需要输入就可以自行决定深度  //  type:结点类型 0 根节点 1左孩子 2右孩子      public static TreeNode createBinaryTree(int type,String parentData) {          switch (type) {          case 0:              System.out.print("根节点:");              break;          case 1:              System.out.print(parentData+"的左孩子:");              break;          case 2:              System.out.print(parentData+"的右孩子:");              break;          }            //      可以使用手动输入也可以放到数组里  //      Scanner sc = new Scanner(System.in);  //      String data = sc.nextLine();                    String data = "";          if(arrayIndex<array.length){              data = array[arrayIndex];              System.out.println(data);                            arrayIndex++;          }else{              System.out.println();          }                    TreeNode node = null;            //      data为空表示没有这个孩子          if(data==null||data.equals("")){              return node;          }else{              node = new TreeNode(data);              node.setLchild(createBinaryTree(1,node.getData()));              node.setRchild(createBinaryTree(2,node.getData()));                            return node;          }                }              //  前序遍历      public static void preOrderTraverse(TreeNode node){          if(node != null){  //          根,左,右              System.out.print(node.getData());              preOrderTraverse(node.getLchild());              preOrderTraverse(node.getRchild());          }      }        //  中序遍历      public static void inOrderTraverse(TreeNode node){          if(node != null){  //          左,根,右              inOrderTraverse(node.getLchild());              System.out.print(node.getData());              inOrderTraverse(node.getRchild());          }      }        //  后序遍历      public static void postOrderTraverse(TreeNode node){          if(node != null){  //          左,右,根              postOrderTraverse(node.getLchild());              postOrderTraverse(node.getRchild());              System.out.print(node.getData());          }      }        //                public static void main(String[] args) {          TreeNode rootNode = createBinaryTree(0,"");                    System.out.println();          System.out.print("前序遍历:");          preOrderTraverse(rootNode);                    System.out.println();          System.out.print("中序遍历:");          inOrderTraverse(rootNode);                    System.out.println();          System.out.print("后序遍历:");          postOrderTraverse(rootNode);      }    }    /**  * 二叉树结点  * @author cmdsm  *  */  class TreeNode{      private String data;            private TreeNode lchild;      private TreeNode rchild;                        public TreeNode() {          super();      }        public TreeNode(String data) {          this.data = data;      }        public String getData() {          return data;      }            public void setData(String data) {          this.data = data;      }        public TreeNode getLchild() {          return lchild;      }        public void setLchild(TreeNode lchild) {          this.lchild = lchild;      }        public TreeNode getRchild() {          return rchild;      }        public void setRchild(TreeNode rchild) {          this.rchild = rchild;      }        @Override      public String toString() {          return "TreeNode [data=" + data + ", lchild=" + lchild + ", rchild=" + rchild + "]";      }                    }  

结果:

三: 

线索二叉树(中序)

代码所示为下图二叉树

中序遍历:CBDAEF

C,D,F有两个空指针域,E有一个


步骤如下:

1.创建二叉树

2.创建头结点

3.中序遍历线索化

4.中序遍历此线索二叉树(非递归方式)

[java] view plain copy
  1. public class ThreadedBinaryTree {  
  2.       
  3.     private static String [] array = {"A","B","C","","","D","","","E","","F","",""};  
  4.     private static int arrayIndex = 0;  
  5.       
  6.     /** 
  7.      * 全局node,始终指向刚刚访问过的结点 
  8.      */  
  9.     private static ThreadedBinaryNode preNode;  
  10.       
  11.     /** 
  12.      * 1.参考创建二叉树,前序遍历输入 
  13.      */  
  14.     public static ThreadedBinaryNode createThreadedBinaryTree(){  
  15.         String data = "";  
  16.         if(arrayIndex<array.length){  
  17.             data = array[arrayIndex];  
  18.               
  19.             arrayIndex++;  
  20.         }  
  21.           
  22.         ThreadedBinaryNode node = null;  
  23.           
  24. //      data为空表示没有这个孩子  
  25.         if(data==null||data.equals("")){  
  26.             return node;  
  27.         }else{  
  28.             node = new ThreadedBinaryNode(data);  
  29.             node.setLchild(createThreadedBinaryTree());  
  30.             node.setRchild(createThreadedBinaryTree());  
  31.               
  32.             node.setLtag(PointerTag.LINK);  
  33.             node.setRtag(PointerTag.LINK);  
  34.               
  35.             return node;  
  36.         }  
  37.     }  
  38.       
  39.     /** 
  40.      * 2.创建头结点,左孩子指向根节点 
  41.      * @param rootNode 
  42.      */  
  43.     public static ThreadedBinaryNode createHeadNode(ThreadedBinaryNode rootNode){  
  44.         ThreadedBinaryNode headNode = new ThreadedBinaryNode();  
  45.           
  46.         headNode.setLtag(PointerTag.LINK);  
  47.         headNode.setRtag(PointerTag.THREAD);  
  48.           
  49. //      右孩子先指向自己,如果根节点不为null,指向中序遍历的最后一个结点,为null不用变  
  50.         headNode.setRchild(headNode);  
  51.           
  52.         if(rootNode != null){  
  53. //          根结点不为null,头结点的左孩子指向根结点  
  54.             headNode.setLchild(rootNode);  
  55.               
  56.             preNode = headNode;  
  57.               
  58. //          开始中序遍历根结点  
  59.             inOrderTraverse(rootNode);  
  60.               
  61. //          中序遍历的最后一个结点的后继指向头结点  
  62.             preNode.setRtag(PointerTag.THREAD);  
  63.             preNode.setRchild(headNode);  
  64.               
  65. //          头结点的右孩子指向最后一个结点  
  66.             headNode.setRchild(preNode);  
  67.               
  68.         }else{  
  69. //          根节点为null 左孩子指向自己  
  70.             headNode.setLchild(headNode);  
  71.         }  
  72.           
  73.         return headNode;  
  74.     }  
  75.       
  76.     /** 
  77.      * 3.中序遍历线索化 
  78.      */  
  79.     public static void inOrderTraverse(ThreadedBinaryNode node){  
  80.         if(node != null){  
  81. //          递归左孩子线索化  
  82.             inOrderTraverse(node.getLchild());  
  83.               
  84. //          结点处理  
  85.             if(null == node.getLchild()){  
  86. //              如果左孩子为空,设置tag为线索 THREAD,并把lchild指向刚刚访问的结点  
  87.                 node.setLtag(PointerTag.THREAD);  
  88.                 node.setLchild(preNode);  
  89.             }  
  90.               
  91.             if(null == preNode.getRchild()){  
  92. //              如果preNode的右孩子为空,设置tag为线索THREAD  
  93.                 preNode.setRtag(PointerTag.THREAD);  
  94.                 preNode.setRchild(node);  
  95.             }  
  96.               
  97. //          此处和前后两个递归的顺序不能改变,和结点处理同属一个级别  
  98.             preNode = node;  
  99. //          System.out.print(node.getData());  
  100.               
  101. //          递归右孩子线索化  
  102.             inOrderTraverse(node.getRchild());  
  103.               
  104.         }  
  105.     }  
  106.       
  107.     /** 
  108.      * 4.中序遍历 非递归方式 
  109.      * @param headNode 
  110.      */  
  111.     public static void inOrderTraverseNotRecursion(ThreadedBinaryNode headNode){  
  112.         ThreadedBinaryNode node = headNode.getLchild();  
  113.           
  114.         while(headNode != node){  
  115.               
  116. //          最左  
  117.             while(node.getLtag() == PointerTag.LINK){  
  118.                 node = node.getLchild();  
  119.             }  
  120.               
  121.             System.out.print(node.getData());  
  122.               
  123. //          根  
  124.             while(node.getRtag() == PointerTag.THREAD && node.getRchild() !=headNode){  
  125.                 node = node.getRchild();  
  126.                   
  127.                 System.out.print(node.getData());  
  128.             }  
  129.               
  130. //          右,不能打印是因为该子树下可能还存在最左  
  131.             node = node.getRchild();  
  132.         }  
  133.     }  
  134.       
  135.       
  136.       
  137.     public static void main(String[] args) {  
  138. //      创建二叉树,约定前序输入  
  139.         ThreadedBinaryNode rootNode = createThreadedBinaryTree();  
  140. //      创建头结点,并中序遍历线索化  
  141.         ThreadedBinaryNode headNode = createHeadNode(rootNode);  
  142. //      中序遍历 非递归方式输出  
  143.         inOrderTraverseNotRecursion(headNode);  
  144.     }  
  145.   
  146. }  
  147.   
  148. class ThreadedBinaryNode{  
  149.     private String data;  
  150.       
  151.     private ThreadedBinaryNode lchild;  
  152.     private ThreadedBinaryNode rchild;  
  153.       
  154.     private PointerTag ltag;  
  155.     private PointerTag rtag;  
  156.       
  157.       
  158.     public String getData() {  
  159.         return data;  
  160.     }  
  161.     public void setData(String data) {  
  162.         this.data = data;  
  163.     }  
  164.     public ThreadedBinaryNode getLchild() {  
  165.         return lchild;  
  166.     }  
  167.     public void setLchild(ThreadedBinaryNode lchild) {  
  168.         this.lchild = lchild;  
  169.     }  
  170.     public ThreadedBinaryNode getRchild() {  
  171.         return rchild;  
  172.     }  
  173.     public void setRchild(ThreadedBinaryNode rchild) {  
  174.         this.rchild = rchild;  
  175.     }  
  176.     public PointerTag getLtag() {  
  177.         return ltag;  
  178.     }  
  179.     public void setLtag(PointerTag ltag) {  
  180.         this.ltag = ltag;  
  181.     }  
  182.     public PointerTag getRtag() {  
  183.         return rtag;  
  184.     }  
  185.     public void setRtag(PointerTag rtag) {  
  186.         this.rtag = rtag;  
  187.     }  
  188.       
  189.       
  190.     public ThreadedBinaryNode(String data) {  
  191.         super();  
  192.         this.data = data;  
  193.     }  
  194.     public ThreadedBinaryNode() {  
  195.         super();  
  196.     }  
  197.     @Override  
  198.     public String toString() {  
  199.         return "ThreadedBinaryNode [data=" + data + ", ltag=" + ltag  
  200.                 + ", rtag=" + rtag + "]";  
  201.     }  
  202.       
  203.       
  204.       
  205. }  
  206.   
  207. /** 
  208.  * LINK :表示指向左右孩子的指针 
  209.  * THREAD:表示指向前驱后继的线索 
  210.  * @author cmdsm 
  211.  * 
  212.  */  
  213. enum PointerTag{  
  214.     LINK , THREAD  
  215. }  

四: 

 

普里姆(Prim)算法

个人认为此算法遍历顺序的决定条件:

1.确定第一个顶点

2.下一个顶点可到(小于正无穷)

3.取可到顶点中最小权值的一个


代码中的图


最小生成树:99



代码(参考其他文章):

[java] view plain copy
  1. public class MinSpanTree {  
  2.     /** 邻接矩阵*/  
  3.     int[][] matrix;  
  4.     /** 表示正无穷*/  
  5.     int MAX_WEIGHT = Integer.MAX_VALUE;  
  6.     /** 顶点个数*/  
  7.     int size;  
  8.   
  9.     /** 
  10.      * 普里姆算法实现最小生成树:先初始化拿到第一个顶点相关联的权值元素放到数组中-》找到其中权值最小的顶点下标-》再根据该下标,将该下标顶点相关联的权值加入到数组中-》循环遍历处理 
  11.      */  
  12.     public void prim() {  
  13.         /**存放当前到全部顶点最小权值的数组,如果已经遍历过的顶点权值为0,无法到达的为正无穷*/  
  14.         int[] tempWeight = new int[size];  
  15.         /**当前到下一个最小权值顶点的最小权值*/  
  16.         int minWeight;  
  17.         /**当前到下一个最小权值的顶点*/  
  18.         int minId;  
  19.         /**权值总和*/  
  20.         int sum = 0;  
  21.           
  22.         //第一个顶点时,到其他顶点的权值即为邻接矩阵的第一行  
  23.         for (int i = 0; i < size; i++) {  
  24.             tempWeight[i] = matrix[0][i];  
  25.         }  
  26.   
  27.         System.out.println("从顶点v0开始查找");  
  28.         for (int i = 1; i < size; i++) {  
  29.             // 每次循环找出当前到下一个最小权值的顶点极其最小权值   
  30.             minWeight = MAX_WEIGHT;  
  31.             minId = 0;  
  32.             for (int j = 1; j < size; j++) {  
  33.                 //权值为0的顶点已经遍历过,不再计入  
  34.                 if (tempWeight[j] > 0 && tempWeight[j] < minWeight) {  
  35.                     minWeight = tempWeight[j];  
  36.                     minId = j;  
  37.                 }  
  38.             }  
  39.               
  40.             // 找到目标顶点minId,他的权值为minweight。  
  41.             System.out.println("找到顶点:v" + minId + " 权值为:" + minWeight);  
  42.             sum += minWeight;  
  43.               
  44.               
  45.             // 算法核心所在:将目标顶点到各个顶点的权值与当前tempWeight数组中的权值做比较,如果前者比后者到某个顶点的权值更小,将前者到这个顶点的权值更新入后者。  
  46.             tempWeight[minId] = 0;  
  47.             for (int j = 1; j < size; j++) {  
  48.                 if (tempWeight[j] != 0 && matrix[minId][j] < tempWeight[j]) {  
  49.                     tempWeight[j] = matrix[minId][j];  
  50.                 }  
  51.             }  
  52.         }  
  53.         System.out.println("最小权值总和为:" + sum);  
  54.     }  
  55.   
  56.     private void createGraph(int index) {  
  57.         size = index;  
  58.         matrix = new int[index][index];  
  59.         int[] v0 = { 010, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 11, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };  
  60.         int[] v1 = { 10018, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 16, MAX_WEIGHT, 12 };  
  61.         int[] v2 = { MAX_WEIGHT, 18022, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 8 };  
  62.         int[] v3 = { MAX_WEIGHT, MAX_WEIGHT, 22020, MAX_WEIGHT, MAX_WEIGHT, 1621 };  
  63.         int[] v4 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 20026, MAX_WEIGHT, 7, MAX_WEIGHT };  
  64.         int[] v5 = { 11, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 26017, MAX_WEIGHT, MAX_WEIGHT };  
  65.         int[] v6 = { MAX_WEIGHT, 16, MAX_WEIGHT, 24, MAX_WEIGHT, 17019, MAX_WEIGHT };  
  66.         int[] v7 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 167, MAX_WEIGHT, 190, MAX_WEIGHT };  
  67.         int[] v8 = { MAX_WEIGHT, 12821, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 0 };  
  68.         matrix[0] = v0;  
  69.         matrix[1] = v1;  
  70.         matrix[2] = v2;  
  71.         matrix[3] = v3;  
  72.         matrix[4] = v4;  
  73.         matrix[5] = v5;  
  74.         matrix[6] = v6;  
  75.         matrix[7] = v7;  
  76.         matrix[8] = v8;  
  77.     }  
  78.   
  79.     public static void main(String[] args) {  
  80.         MinSpanTree graph = new MinSpanTree();  
  81.         graph.createGraph(9);  
  82.         graph.prim();  
  83.     }  
  84.   
  85. }  

 五:克鲁斯卡尔(Kruskal)算法

判断是否为回路的机制没有理解

代码所示图和边集数组


代码

[java] view plain copy
  1. public class MiniSpanTreeKruskal {  
  2.   
  3.     /** 邻接矩阵 */  
  4.     private int[][] matrix;  
  5.     /** 表示正无穷 */  
  6.     private int MAX_WEIGHT = Integer.MAX_VALUE;  
  7.     /**边集数组*/  
  8.     private List<Edge> edgeList = new ArrayList<Edge>();  
  9.   
  10.   
  11.     /** 
  12.      * 创建图 
  13.      */  
  14.     private void createGraph(int index) {  
  15.         matrix = new int[index][index];  
  16.         int[] v0 = { 010, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 11, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };  
  17.         int[] v1 = { 10018, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 16, MAX_WEIGHT, 12 };  
  18.         int[] v2 = { MAX_WEIGHT, 18022, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 8 };  
  19.         int[] v3 = { MAX_WEIGHT, MAX_WEIGHT, 22020, MAX_WEIGHT, MAX_WEIGHT, 1621 };  
  20.         int[] v4 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 20026, MAX_WEIGHT, 7, MAX_WEIGHT };  
  21.         int[] v5 = { 11, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 26017, MAX_WEIGHT, MAX_WEIGHT };  
  22.         int[] v6 = { MAX_WEIGHT, 16, MAX_WEIGHT, 24, MAX_WEIGHT, 17019, MAX_WEIGHT };  
  23.         int[] v7 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 167, MAX_WEIGHT, 190, MAX_WEIGHT };  
  24.         int[] v8 = { MAX_WEIGHT, 12821, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 0 };  
  25.         matrix[0] = v0;  
  26.         matrix[1] = v1;  
  27.         matrix[2] = v2;  
  28.         matrix[3] = v3;  
  29.         matrix[4] = v4;  
  30.         matrix[5] = v5;  
  31.         matrix[6] = v6;  
  32.         matrix[7] = v7;  
  33.         matrix[8] = v8;  
  34.     }  
  35.       
  36.     /** 
  37.      * 创建边集数组,并且对他们按权值从小到大排序(顺序存储结构也可以认为是数组吧) 
  38.      */  
  39.     public void createEdages() {  
  40.   
  41.         Edge v0 = new Edge(477);  
  42.         Edge v1 = new Edge(288);  
  43.         Edge v2 = new Edge(0110);  
  44.         Edge v3 = new Edge(0511);  
  45.         Edge v4 = new Edge(1812);  
  46.         Edge v5 = new Edge(3716);  
  47.         Edge v6 = new Edge(1616);  
  48.         Edge v7 = new Edge(5617);  
  49.         Edge v8 = new Edge(1218);  
  50.         Edge v9 = new Edge(6719);  
  51.         Edge v10 = new Edge(3420);  
  52.         Edge v11 = new Edge(3821);  
  53.         Edge v12 = new Edge(2322);  
  54.         Edge v13 = new Edge(3624);  
  55.         Edge v14 = new Edge(4526);  
  56.   
  57.         edgeList.add(v0);  
  58.         edgeList.add(v1);  
  59.         edgeList.add(v2);  
  60.         edgeList.add(v3);  
  61.         edgeList.add(v4);  
  62.         edgeList.add(v5);  
  63.         edgeList.add(v6);  
  64.         edgeList.add(v7);  
  65.         edgeList.add(v8);  
  66.         edgeList.add(v9);  
  67.         edgeList.add(v10);  
  68.         edgeList.add(v11);  
  69.         edgeList.add(v12);  
  70.         edgeList.add(v13);  
  71.         edgeList.add(v14);  
  72.     }  
  73.   
  74.     // 克鲁斯卡尔算法  
  75.     public void kruskal() {  
  76.         //创建图和边集数组  
  77.         createGraph(9);  
  78.         //可以由图转出边集数组并按权从小到大排序,这里为了方便观察直接写出来了  
  79.         createEdages();  
  80.           
  81.         //定义一个数组用来判断边与边是否形成环路  
  82.         int[] parent = new int[9];  
  83.           
  84.         /**权值总和*/  
  85.         int sum = 0;  
  86.           
  87.         int n, m;  
  88.           
  89.         //遍历边  
  90.         for (int i = 0; i < edgeList.size(); i++) {  
  91.             Edge edge= edgeList.get(i);  
  92.             n = find(parent, edge.getBegin());  
  93.             m = find(parent, edge.getEnd());  
  94.               
  95.             //说明形成了环路或者两个结点都在一棵树上  
  96.             //注:书上没有讲解为什么这种机制可以保证形成环路,思考了半天,百度了也没有什么好的答案,研究的时间不多,就暂时就放一放吧  
  97.             if (n != m) {  
  98.                 parent[n] = m;  
  99.                 System.out.println("(" + edge.getBegin() + "," + edge.getEnd() + ")" +edge.getWeight());  
  100.                   
  101.                 sum += edge.getWeight();  
  102.             }  
  103.         }  
  104.           
  105.         System.out.println("权值总和为:" + sum);  
  106.     }  
  107.   
  108.     public int find(int[] parent, int index) {  
  109.         while (parent[index] > 0) {  
  110.             index = parent[index];  
  111.         }  
  112.         return index;  
  113.     }  
  114.   
  115.     public static void main(String[] args) {  
  116.         MiniSpanTreeKruskal graph = new MiniSpanTreeKruskal();  
  117.         graph.kruskal();  
  118.     }  
  119.   
  120. }  
  121.   
  122. class Edge {  
  123.   
  124.     private int begin;  
  125.     private int end;  
  126.     private int weight;  
  127.   
  128.     public Edge(int begin, int end, int weight) {  
  129.         super();  
  130.         this.begin = begin;  
  131.         this.end = end;  
  132.         this.weight = weight;  
  133.     }  
  134.   
  135.     public int getBegin() {  
  136.         return begin;  
  137.     }  
  138.   
  139.     public void setBegin(int begin) {  
  140.         this.begin = begin;  
  141.     }  
  142.   
  143.     public int getEnd() {  
  144.         return end;  
  145.     }  
  146.   
  147.     public void setEnd(int end) {  
  148.         this.end = end;  
  149.     }  
  150.   
  151.     public int getWeight() {  
  152.         return weight;  
  153.     }  
  154.   
  155.     public void setWeight(int weight) {  
  156.         this.weight = weight;  
  157.     }  
  158.   
  159.     @Override  
  160.     public String toString() {  
  161.         return "Edge [begin=" + begin + ", end=" + end + ", weight=" + weight + "]";  
  162.     }  
  163.       
  164.       
  165.   
  166. }  
结果:

六: 

迪杰斯特拉(Dijkstra)算法

基本思想

     通过Dijkstra计算图G中的最短路径时,需要指定起点vs(即从顶点vs开始计算)。

     此外,引进两个集合S和U。S的作用是记录已求出最短路径的顶点,而U则是记录还未求出最短路径的顶点(以及该顶点到起点vs的距离)。

     初始时,S中只有起点vs;U中是除vs之外的顶点,并且U中顶点的路径是"起点vs到该顶点的路径"。然后,从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 然后,再从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 ... 重复该操作,直到遍历完所有顶点。


操作步骤

(1) 初始时,S只包含起点vs;U包含除vs外的其他顶点,且U中顶点的距离为"起点vs到该顶点的距离"[例如,U中顶点v的距离为(vs,v)的长度,然后vs和v不相邻,则v的距离为∞]。

(2) 从U中选出"距离最短的顶点k",并将顶点k加入到S中;同时,从U中移除顶点k。

(3) 更新U中各个顶点到起点vs的距离。之所以更新U中顶点的距离,是由于上一步中确定了k是求出最短路径的顶点,从而可以利用k来更新其它顶点的距离;例如,(vs,v)的距离可能大于(vs,k)+(k,v)的距离。

(4) 重复步骤(2)和(3),直到遍历完所有顶点。



代码示例图:

图一:


图二:


代码:

[java] view plain copy
  1. public class ShortestPathDijkstra {  
  2.     /** 邻接矩阵 */  
  3.     private int[][] matrix;  
  4.     /** 表示正无穷 */  
  5.     private int MAX_WEIGHT = Integer.MAX_VALUE;  
  6.     /** 顶点集合 */  
  7.     private String[] vertexes;  
  8.   
  9.     /** 
  10.      * 创建图2 
  11.      */  
  12.     private void createGraph2(int index) {  
  13.         matrix = new int[index][index];  
  14.         vertexes = new String[index];  
  15.           
  16.         int[] v0 = { 015, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };  
  17.         int[] v1 = { 10375, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };  
  18.         int[] v2 = { 530, MAX_WEIGHT, 17, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };  
  19.         int[] v3 = { MAX_WEIGHT, 7, MAX_WEIGHT, 02, MAX_WEIGHT, 3, MAX_WEIGHT, MAX_WEIGHT };  
  20.         int[] v4 = { MAX_WEIGHT, 5120369, MAX_WEIGHT };  
  21.         int[] v5 = { MAX_WEIGHT, MAX_WEIGHT, 7, MAX_WEIGHT, 30, MAX_WEIGHT, 5, MAX_WEIGHT };  
  22.         int[] v6 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 36, MAX_WEIGHT, 027 };  
  23.         int[] v7 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 95204 };  
  24.         int[] v8 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 740 };  
  25.         matrix[0] = v0;  
  26.         matrix[1] = v1;  
  27.         matrix[2] = v2;  
  28.         matrix[3] = v3;  
  29.         matrix[4] = v4;  
  30.         matrix[5] = v5;  
  31.         matrix[6] = v6;  
  32.         matrix[7] = v7;  
  33.         matrix[8] = v8;  
  34.           
  35.         vertexes[0] = "v0";  
  36.         vertexes[1] = "v1";  
  37.         vertexes[2] = "v2";  
  38.         vertexes[3] = "v3";  
  39.         vertexes[4] = "v4";  
  40.         vertexes[5] = "v5";  
  41.         vertexes[6] = "v6";  
  42.         vertexes[7] = "v7";  
  43.         vertexes[8] = "v8";  
  44.     }  
  45.       
  46.     /** 
  47.      * 创建图1 
  48.      */  
  49.     private void createGraph1(int index) {  
  50.         matrix = new int[index][index];  
  51.         vertexes = new String[index];  
  52.   
  53.         int[] v0 = { 01, MAX_WEIGHT, MAX_WEIGHT, 2, MAX_WEIGHT };  
  54.         int[] v1 = { 101, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };  
  55.         int[] v2 = { MAX_WEIGHT, 101, MAX_WEIGHT, MAX_WEIGHT };  
  56.         int[] v3 = { MAX_WEIGHT, MAX_WEIGHT, 101, MAX_WEIGHT };  
  57.         int[] v4 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 101 };  
  58.         int[] v5 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 110 };  
  59.   
  60.         matrix[0] = v0;  
  61.         matrix[1] = v1;  
  62.         matrix[2] = v2;  
  63.         matrix[3] = v3;  
  64.         matrix[4] = v4;  
  65.         matrix[5] = v5;  
  66.   
  67.         vertexes[0] = "A";  
  68.         vertexes[1] = "B";  
  69.         vertexes[2] = "C";  
  70.         vertexes[3] = "D";  
  71.         vertexes[4] = "E";  
  72.         vertexes[5] = "F";  
  73.     }  
  74.   
  75.     /** 
  76.      * Dijkstra最短路径。 
  77.      *  
  78.      * vs -- 起始顶点(start vertex) 即,统计图中"顶点vs"到其它各个顶点的最短路径。 
  79.      */  
  80.     public void dijkstra(int vs) {  
  81.         // flag[i]=true表示"顶点vs"到"顶点i"的最短路径已成功获取  
  82.         boolean[] flag = new boolean[vertexes.length];  
  83.         // U则是记录还未求出最短路径的顶点(以及该顶点到起点s的距离),与 flag配合使用,flag[i] == true 表示U中i顶点已被移除  
  84.         int[] U = new int[vertexes.length];  
  85.         // 前驱顶点数组,即,prev[i]的值是"顶点vs"到"顶点i"的最短路径所经历的全部顶点中,位于"顶点i"之前的那个顶点。  
  86.         int[] prev = new int[vertexes.length];  
  87.         // S的作用是记录已求出最短路径的顶点  
  88.         String[] S = new String[vertexes.length];  
  89.   
  90.         // 步骤一:初始时,S中只有起点vs;U中是除vs之外的顶点,并且U中顶点的路径是"起点vs到该顶点的路径"。  
  91.         for (int i = 0; i < vertexes.length; i++) {  
  92.             flag[i] = false// 顶点i的最短路径还没获取到。  
  93.             U[i] = matrix[vs][i]; // 顶点i与顶点vs的初始距离为"顶点vs"到"顶点i"的权。也就是邻接矩阵vs行的数据。  
  94.               
  95.             prev[i] = 0//顶点i的前驱顶点为0  
  96.         }  
  97.   
  98.         // 将vs从U中“移除”(U与flag配合使用)  
  99.         flag[vs] = true;  
  100.         U[vs] = 0;  
  101.         // 将vs顶点加入S  
  102.         S[0] = vertexes[vs];  
  103.         // 步骤一结束  
  104.           
  105.         //步骤四:重复步骤二三,直到遍历完所有顶点。  
  106.         // 遍历vertexes.length-1次;每次找出一个顶点的最短路径。  
  107.         int k = 0;  
  108.         for (int i = 1; i < vertexes.length; i++) {  
  109.             // 步骤二:从U中找出路径最短的顶点,并将其加入到S中(如果vs顶点到x顶点还有更短的路径的话,那么  
  110.             // 必然会有一个y顶点到vs顶点的路径比前者更短且没有加入S中  
  111.             // 所以,U中路径最短顶点的路径就是该顶点的最短路径)  
  112.             // 即,在未获取最短路径的顶点中,找到离vs最近的顶点(k)。  
  113.             int min = MAX_WEIGHT;  
  114.             for (int j = 0; j < vertexes.length; j++) {  
  115.                 if (flag[j] == false && U[j] < min) {  
  116.                     min = U[j];  
  117.                     k = j;  
  118.                 }  
  119.             }  
  120.               
  121.             //将k放入S中  
  122.             S[i] = vertexes[k];  
  123.               
  124.             //步骤二结束  
  125.               
  126.               
  127.             //步骤三:更新U中的顶点和顶点对应的路径  
  128.             //标记"顶点k"为已经获取到最短路径(更新U中的顶点,即将k顶点对应的flag标记为true)  
  129.             flag[k] = true;  
  130.               
  131.             //修正当前最短路径和前驱顶点(更新U中剩余顶点对应的路径)  
  132.             //即,当已经"顶点k的最短路径"之后,更新"未获取最短路径的顶点的最短路径和前驱顶点"。  
  133.             for (int j = 0; j < vertexes.length; j++) {  
  134.                 //以k顶点所在位置连线其他顶点,判断其他顶点经过最短路径顶点k到达vs顶点是否小于目前的最短路径,是,更新入U,不是,不做处理  
  135.                 int tmp = (matrix[k][j] == MAX_WEIGHT ? MAX_WEIGHT : (min + matrix[k][j]));  
  136.                 if (flag[j] == false && (tmp < U[j])) {  
  137.                     U[j] = tmp;  
  138.                     //更新 j顶点的最短路径前驱顶点为k  
  139.                     prev[j] = k;  
  140.                 }  
  141.             }  
  142.             //步骤三结束  
  143.         }  
  144.         //步骤四结束  
  145.   
  146.         // 打印dijkstra最短路径的结果  
  147.         System.out.println("起始顶点:" + vertexes[vs]);  
  148.         for (int i = 0; i < vertexes.length; i++) {  
  149.             System.out.print("最短路径(" + vertexes[vs] + "," + vertexes[i] + "):" + U[i] + "  ");  
  150.               
  151.             List<String> path = new ArrayList<>();  
  152.             int j = i;  
  153.             while (true) {  
  154.                 path.add(vertexes[j]);  
  155.                   
  156.                 if (j == 0)  
  157.                     break;  
  158.                   
  159.                 j = prev[j];  
  160.             }  
  161.               
  162.             for (int x = path.size()-1; x >= 0; x--) {  
  163.                 if (x == 0) {  
  164.                     System.out.println(path.get(x));  
  165.                 } else {  
  166.                     System.out.print(path.get(x) + "->");  
  167.                 }  
  168.             }  
  169.               
  170.         }  
  171.           
  172.         System.out.println("顶点放入S中的顺序:");  
  173.         for (int i = 0; i< vertexes.length; i++) {  
  174.               
  175.             System.out.print(S[i]);  
  176.               
  177.             if (i != vertexes.length-1)   
  178.                 System.out.print("-->");  
  179.         }  
  180.               
  181.     }  
  182.   
  183.     public static void main(String[] args) {  
  184.         ShortestPathDijkstra dij = new ShortestPathDijkstra();  
  185.         dij.createGraph1(6);  
  186. //        dij.createGraph2(9);  
  187.         dij.dijkstra(0);  
  188.     }  
  189.   
  190. }  
运行结果:

图一


图二

七: 

弗洛伊德(Floyd)算法

代码所示图:

图1:


图2:


代码:

[java] view plain copy
  1. public class ShortestPathFloyd {  
  2.   
  3.     /** 邻接矩阵 */  
  4.     private int[][] matrix;  
  5.     /** 表示正无穷 */  
  6.     private int MAX_WEIGHT = Integer.MAX_VALUE;  
  7.     /**路径矩阵*/  
  8.     private int[][] pathMatirx;  
  9.     /**前驱表*/  
  10.     private int[][] preTable;  
  11.   
  12.     /** 
  13.      * 创建图2 
  14.      */  
  15.     private void createGraph2(int index) {  
  16.         matrix = new int[index][index];  
  17.   
  18.         int[] v0 = { 015, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };  
  19.         int[] v1 = { 10375, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };  
  20.         int[] v2 = { 530, MAX_WEIGHT, 17, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };  
  21.         int[] v3 = { MAX_WEIGHT, 7, MAX_WEIGHT, 02, MAX_WEIGHT, 3, MAX_WEIGHT, MAX_WEIGHT };  
  22.         int[] v4 = { MAX_WEIGHT, 5120369, MAX_WEIGHT };  
  23.         int[] v5 = { MAX_WEIGHT, MAX_WEIGHT, 7, MAX_WEIGHT, 30, MAX_WEIGHT, 5, MAX_WEIGHT };  
  24.         int[] v6 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 36, MAX_WEIGHT, 027 };  
  25.         int[] v7 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 95204 };  
  26.         int[] v8 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 740 };  
  27.         matrix[0] = v0;  
  28.         matrix[1] = v1;  
  29.         matrix[2] = v2;  
  30.         matrix[3] = v3;  
  31.         matrix[4] = v4;  
  32.         matrix[5] = v5;  
  33.         matrix[6] = v6;  
  34.         matrix[7] = v7;  
  35.         matrix[8] = v8;  
  36.   
  37.     }  
  38.   
  39.     /** 
  40.      * 创建图1 
  41.      */  
  42.     private void createGraph1(int index) {  
  43.         matrix = new int[index][index];  
  44.   
  45.         int[] v0 = { 01, MAX_WEIGHT, MAX_WEIGHT, 2, MAX_WEIGHT };  
  46.         int[] v1 = { 101, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };  
  47.         int[] v2 = { MAX_WEIGHT, 101, MAX_WEIGHT, MAX_WEIGHT };  
  48.         int[] v3 = { MAX_WEIGHT, MAX_WEIGHT, 101, MAX_WEIGHT };  
  49.         int[] v4 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 101 };  
  50.         int[] v5 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 110 };  
  51.   
  52.         matrix[0] = v0;  
  53.         matrix[1] = v1;  
  54.         matrix[2] = v2;  
  55.         matrix[3] = v3;  
  56.         matrix[4] = v4;  
  57.         matrix[5] = v5;  
  58.   
  59.     }  
  60.       
  61.       
  62.     public void floyd(){  
  63.         //路径矩阵(D),表示顶点到顶点的最短路径权值之和的矩阵,初始时,就是图的邻接矩阵。  
  64.         pathMatirx = new int[matrix.length][matrix.length];  
  65.         //前驱表(P),P[m][n] 的值为 m到n的最短路径的前驱顶点,如果是直连,值为n。也就是初始值  
  66.         preTable = new int[matrix.length][matrix.length];  
  67.           
  68.         //初始化D,P  
  69.         for (int i = 0; i < matrix.length; i++) {  
  70.             for (int j = 0; j < matrix.length; j++) {  
  71.                 pathMatirx[i][j] = matrix[i][j];  
  72.                 preTable[i][j] = j;  
  73.             }  
  74.         }  
  75.           
  76.         //循环 中间经过顶点  
  77.         for (int k = 0; k < matrix.length; k++) {  
  78.             //循环所有路径  
  79.             for (int m = 0; m < matrix.length; m++) {  
  80.                   
  81.                 for (int n = 0; n < matrix.length; n++) {  
  82.                       
  83.                     int mn = pathMatirx[m][n];  
  84.                     int mk = pathMatirx[m][k];  
  85.                     int kn = pathMatirx[k][n];  
  86.                     int addedPath = (mk == MAX_WEIGHT || kn == MAX_WEIGHT)? MAX_WEIGHT : mk + kn;  
  87.                       
  88.                     if (mn > addedPath) {  
  89.                         //如果经过k顶点路径比原两点路径更短,将两点间权值设为更小的一个  
  90.                         pathMatirx[m][n] = addedPath;  
  91.                         //前驱设置为经过下标为k的顶点  
  92.                         preTable[m][n] = preTable[m][k];  
  93.                     }  
  94.                       
  95.                 }  
  96.             }  
  97.         }  
  98.     }  
  99.       
  100.     /** 
  101.      * 打印 所有最短路径 
  102.      */  
  103.     public void print() {  
  104.           
  105.         for (int m = 0; m < matrix.length; m++) {  
  106.             for (int n = m + 1; n < matrix.length; n++) {  
  107.                   
  108.                 int k = preTable[m][n];  
  109.                 System.out.print("(" + m + "," + n + ")" + pathMatirx[m][n] + ":  ");  
  110.                 System.out.print(m);  
  111.                 while (k != n) {  
  112.                     System.out.print("->" + k);  
  113.                     k = preTable[k][n];  
  114.                 }  
  115.                   
  116.                 System.out.println("->" + n);  
  117.             }  
  118.             System.out.println();  
  119.         }  
  120.           
  121.           
  122.     }  
  123.       
  124.     public static void main(String[] args) {  
  125.         ShortestPathFloyd floyd = new ShortestPathFloyd();  
  126.         floyd.createGraph2(9);  
  127. //        floyd.createGraph1(6);  
  128.           
  129.         floyd.floyd();  
  130.           
  131.         floyd.print();  
  132.           
  133.     }  


结果:

图1:

图2:

[html] view plain copy
  1. (0,1)1:  0->1  
  2. (0,2)4:  0->1->2  
  3. (0,3)7:  0->1->2->4->3  
  4. (0,4)5:  0->1->2->4  
  5. (0,5)8:  0->1->2->4->5  
  6. (0,6)10:  0->1->2->4->3->6  
  7. (0,7)12:  0->1->2->4->3->6->7  
  8. (0,8)16:  0->1->2->4->3->6->7->8  
  9.   
  10. (1,2)3:  1->2  
  11. (1,3)6:  1->2->4->3  
  12. (1,4)4:  1->2->4  
  13. (1,5)7:  1->2->4->5  
  14. (1,6)9:  1->2->4->3->6  
  15. (1,7)11:  1->2->4->3->6->7  
  16. (1,8)15:  1->2->4->3->6->7->8  
  17.   
  18. (2,3)3:  2->4->3  
  19. (2,4)1:  2->4  
  20. (2,5)4:  2->4->5  
  21. (2,6)6:  2->4->3->6  
  22. (2,7)8:  2->4->3->6->7  
  23. (2,8)12:  2->4->3->6->7->8  
  24.   
  25. (3,4)2:  3->4  
  26. (3,5)5:  3->4->5  
  27. (3,6)3:  3->6  
  28. (3,7)5:  3->6->7  
  29. (3,8)9:  3->6->7->8  
  30.   
  31. (4,5)3:  4->5  
  1. (4,6)5:  4->3->6  
  1. (4,7)7:  4->3->6->7  
  2. (4,8)11:  4->3->6->7->8  
  3.   
  4. (5,6)7:  5->7->6  
  5. (5,7)5:  5->7  
  6. (5,8)9:  5->7->8  
  7.   
  8. (6,7)2:  6->7  
  9. (6,8)6:  6->7->8  
  10.   
  11. (7,8)4:  7->8  

1 0
原创粉丝点击