zoukankan      html  css  js  c++  java
  • D16-常用十种算法[Java数据结构和算法]

    1.二分查找(非递归)代码实现

    1 //实现查找的非递归查找 2 /** 3 * 4 * @param arr 待查找的数组,arr是升序排序 5 * @param target 需要查找的数 6 * @return 返回对应的下标,-1表示没找到 7 */ 8 public static int binarySearch(int[] arr,int target) { 9 int left=0; 10 int right=arr.length-1; 11 while(left<=right) { 12 int mid=(left+right)/2; 13 if(arr[mid]==target) { 14 return mid; 15 }else if(arr[mid]>target) { 16 right=mid-1; 17 }else { 18 left=mid+1; 19 } 20 } 21 return -1; 22 }

    2.分治算法(Divide-and Conquer)

      2.1 分治算法的思想

      (1)把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题....直到最后子问题可以简单直接求解,原问题的解即子问题的解的合并;

      (2)分治算法可以求解的经典问题:二分搜索,大整数乘法,棋盘覆盖,合并排序,快速排序,线性时间选择,最接近点对问题,循环赛日程表,汉诺塔等;

      2.2 分治算法的基本步骤

      (1)分解:将原问题分解为若干个较小,相互独立,与原问题形式相同的子问题;

      (2)解决:将子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;

      (3)合并:将各个子问题的解合并为原问题的解;

      2.3 分治算法的最佳实现-汉诺塔问题

      (1)如果只有一个盘,A->C

       如果有n>=2情况,踪师可以看作是两个盘1,最下面的盘2,上面的盘

        1)先把最上面的盘A->B

        2)把最下面的盘A->C

        3)把B塔中所有的盘从B->C

      (2)源代码

     1 //汉诺塔的移动方法,使用分治算法
     2     public static void hanoiTower(int num,char a,char b,char c) {
     3         //如果只有一个盘
     4         if(num==1) {
     5             System.out.println("第1个盘"+a+"->"+c);
     6         }
     7         if(num>=2) {
     8             //将最上面的所有盘从A->B
     9             hanoiTower(num-1, a, c, b);
    10             //将最下面的一个盘从a->c
    11             System.out.println("第"+num+"个盘"+a+"->"+c);
    12             //把b塔所有的盘给c盘
    13             hanoiTower(num-1, b, a, c);    
    14         }
    15     }

    3.动态规划(Dynamic Programming)

      3.1 动态规划算法基本介绍

      (1)动态规划算法的核心思想是:将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法;

      (2)动态规划的基本思想是将待求解的问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解;

      (3)适合于用动态规划求解的问题,经分解得到子问题往往不是相互独立的,即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解;

      (4)动态规划可以通过填表的方式来逐步推进,得到最优解;

      3.2 动态规划算法的最佳实践-背包问题

      (1)思路分析

      (2)源代码实现

     1 package com.atguigu.dynamic;
     2 
     3 public class KnapsackProblem {
     4 
     5     public static void main(String[] args) {
     6         // TODO Auto-generated method stub
     7         int[] w= {1,4,3};//物品的重量
     8         int[] val= {1500,3000,2000};//物品的价值
     9         int m=4;//背包的容量
    10         int n=val.length;//物品的价值
    11         
    12         
    13         //创建二维数组
    14         //v[i][j] 表示在前i个物品中能够装入容量为j的背包中的最大价值
    15         int[][] v=new int[n+1][m+1];
    16         
    17         //为了记录放入商品的情况,定义一个二维数组
    18         int[][] path=new int[n+1][m+1];
    19         
    20         //初始化第一行和第一列
    21         for(int i=0;i<v.length;i++) {//处理第一列
    22             v[i][0]=0;
    23         }
    24         for(int i=0;i<v[0].length;i++) {//处理第一行
    25             v[0][i]=0;
    26         }
    27         
    28         for(int i=1;i<v.length;i++) {//不处理第一行
    29             for(int j=1;j<v[0].length;j++) {
    30                 if(w[i-1]>j) {
    31                     v[i][j]=v[i-1][j];
    32                 }else {
    33 //                    v[i][j]=Math.max(v[i-1][j], val[i-1]+v[i-1][j-w[i-1]]);
    34                     if(v[i-1][j]>val[i-1]+v[i-1][j-w[i-1]]) {
    35                         v[i][j]=v[i-1][j];
    36                     }else {
    37                         v[i][j]=val[i-1]+v[i-1][j-w[i-1]];
    38                         path[i][j]=1;
    39                     }
    40                 }
    41             }
    42         }
    43         
    44         for(int i=0;i<v.length;i++) {
    45             for(int j=0;j<v[0].length;j++) {
    46                 System.out.print(v[i][j]+" ");
    47             }
    48             System.out.println();
    49         }
    50         
    51         //输出最后我们放入说的是哪些商品
    52         int i=path.length-1;//行的最大下标
    53         int j=path[0].length-1;//列的最大下标
    54         while(i>0&&j>0) {
    55             if(path[i][j]==1) {
    56                 System.out.printf("第%d个商品放入到背包
    ",i);
    57                 j-=w[i-1];
    58             }
    59             i--;
    60         }
    61     }
    62 
    63 }

      

    4.KMP算法

      4.1暴力匹配算法

      (1)思路,假设现在str1匹配到i位置,子串str2匹配到j位置,则有:

        1)若当前字符匹配成功,则i++,j++,继续匹配下一个字符;

        2)若失配,令i=i-(j-1),j=0。相当于每次匹配失败时,i回溯,j被置为9;

        3)用暴力方法解决会有大量的回溯,每次只移动一次,若不匹配,移动到下一位接着判断,浪费大量时间;

      (2)算法实现

     1 public static int violenceMatch(String str1,String str2) {
     2         int i=0;
     3         int j=0;
     4         while(i<str1.length()&&j<str2.length()) {
     5             if(str1.charAt(i)==str2.charAt(j)) {//如果匹配就继续
     6                 i++;
     7                 j++;
     8             }else {//如果不匹配
     9                 i=i-(j-1);
    10                 j=0;
    11             }
    12         }
    13         if(j==str2.length()) {
    14             return i-j;
    15         }else {
    16             return -1;
    17         }
    18         
    19     }

      4.2 KMP算法(Knuth-Morris-Pratt)

       (1)解决模式串在文本串中是否出现过,如果出现过,最早出现的位置的经典算法;

      (2)利用之前判断过信息,通过一个next数组,保存模式串中前后最长公共子序列的长度,每次回溯时,通过next数组找到前面匹配过的位置,省去了大量的时间;

      (3)源代码

     1 //写出我们的kmp搜索算法
     2     /**
     3      * 
     4      * @param str1 源字符串
     5      * @param str2 子串
     6      * @param next 部分匹配表,是子串对应的部分匹配表
     7      * @return 如果是-1就是没有匹配到,否则返回第一个匹配的位置
     8      */
     9     public static int kmpSearch(String str1,String str2,int[] next) {
    10         //遍历
    11         for (int i = 0, j=0 ; i < str1.length(); i++) {
    12             
    13             //需要处理str1.charAt(i)!=str2.charAt(j),要去调整j的大小
    14             //KMP算法的核心点
    15             while( j >0 &&  str1.charAt(i)!=str2.charAt(j)) {
    16                 j=next[j-1];
    17             }
    18             if(str1.charAt(i)==str2.charAt(j)) {
    19                 j++;
    20             }
    21             if(j==str2.length()) {//说明找到了
    22                 return i-j+1;
    23             }
    24         }
    25         return -1;
    26     }
    27 
    28     // 获取到一个字符串的部分匹配值表
    29     public static int[] kmpNext(String dest) {
    30         // 创建一个next 数组保存部分匹配值
    31         int[] next = new int[dest.length()];
    32         next[0] = 0;// 如果字符串的长度为1,部分匹配值就是0
    33         for (int i = 1, j = 0; i < dest.length(); i++) {
    34             //当dest.charAt(i)!=dest.charAt(j),需要从next[j-1]获取新的j
    35             //直到我们发现有dest.charAt(i)==dest.charAt(j)成立时才退出
    36             //kmp算法的核心点
    37             while(j>0 && dest.charAt(i)!=dest.charAt(j)) {
    38                 j=next[j-1];
    39             }
    40             // 当dest.charAt(i)==dest.charAt(j)满足时,部分匹配值就是+1
    41             if (dest.charAt(i) == dest.charAt(j)) {
    42                 j++;
    43             }
    44             next[i] = j;
    45         }
    46         return next;
    47     }

     

    5.贪心算法

      5.1 介绍

      (1)贪心算法(贪婪算法)指在问题求解时,在每一步选择中都采取最好或者最优的选择,从而希望能够导致结果是最好或是最优的算法;

      (2)贪婪算法所得到的结果不一定是最优的结果(有时候是最优解),但是都是相对近似(接近)最优解的结果;

      5.2 源代码-集合覆盖问题

     1 package com.atguigu.greedy;
     2 
     3 import java.util.ArrayList;
     4 import java.util.HashMap;
     5 import java.util.HashSet;
     6 
     7 public class GreedyAlgorithm {
     8     public static void main(String[] args) {
     9         //创建广播电台
    10         HashMap<String, HashSet<String>> broadcasts=new HashMap<String, HashSet<String>>();
    11         //将各个电台放入到broadcasts
    12         HashSet<String> hashSet1=new HashSet<String>();
    13         hashSet1.add("北京");
    14         hashSet1.add("上海");
    15         hashSet1.add("天津");
    16         
    17         HashSet<String> hashSet2=new HashSet<String>();
    18         hashSet2.add("广州");
    19         hashSet2.add("上海");
    20         hashSet2.add("深圳");
    21         
    22         HashSet<String> hashSet3=new HashSet<String>();
    23         hashSet3.add("成都");
    24         hashSet3.add("上海");
    25         hashSet3.add("杭州");
    26         
    27         HashSet<String> hashSet4=new HashSet<String>();
    28         hashSet4.add("上海");
    29         hashSet4.add("天津");
    30         
    31         HashSet<String> hashSet5=new HashSet<String>();
    32         hashSet5.add("杭州");
    33         hashSet5.add("大连");
    34         
    35         broadcasts.put("K1", hashSet1);
    36         broadcasts.put("K2", hashSet2);
    37         broadcasts.put("K3", hashSet3);
    38         broadcasts.put("K4", hashSet4);
    39         broadcasts.put("K5", hashSet5);
    40         
    41         HashSet<String> allAreas=new HashSet<String>();//存放所有的城市
    42         allAreas.add("北京");
    43         allAreas.add("上海");
    44         allAreas.add("天津");
    45         allAreas.add("广州");
    46         allAreas.add("深圳");
    47         allAreas.add("成都");
    48         allAreas.add("杭州");
    49         allAreas.add("大连");
    50         
    51         ArrayList<String> SelectedBC=new ArrayList<String>();//存放选择电台的集合
    52         
    53         //定义一个临时的集合,在遍历过程中,存放遍历过程中电台覆盖地区和当前没有覆盖地区的交集
    54         HashSet<String> tempSet=new HashSet<String>();
    55         
    56         //定义maxKey,保存在一次遍历过程中,能够覆盖最大未覆盖地区对应的电台key
    57         //如果maxKey不为null,则会加入到SelectedBC
    58         String maxKey=null;
    59         while(allAreas.size()!=0) {//如果allAreas不为0,则表示还没有覆盖到所有的地区
    60             //每进行一次while,需要
    61             maxKey=null;
    62             //遍历broadcasts,取出对应的key
    63             for(String key:broadcasts.keySet()) {
    64                 //没进行一次for
    65                 tempSet.clear();
    66                 //当前这个key能覆盖的地区
    67                 HashSet<String> areas=broadcasts.get(key);
    68                 tempSet.addAll(areas);
    69                 //求出tempSet和allAreas集合的交集
    70                 tempSet.retainAll(allAreas);
    71                 //如果当前这个集合包含的未覆盖地区的数量,比maxKey指向的集合地区还多
    72                 //tempSet.size()>broadcasts.get(maxKey).size()) 体现出贪心算法的特点
    73                 if(tempSet.size()>0 &&
    74                         (maxKey==null ||tempSet.size()>broadcasts.get(maxKey).size())) {
    75                     maxKey=key;
    76                 }
    77             }
    78             //maxKey!=null,就应该将maxKey加入到SelectedBC
    79             if(maxKey!=null) {
    80                     SelectedBC.add(maxKey);
    81                     //将maxKey指向的广播电台覆盖的地区,从allAreas去掉
    82                     allAreas.removeAll(broadcasts.get(maxKey));
    83                 }
    84         }
    85         System.out.println("得到的选择结果"+SelectedBC);
    86     }
    87     
    88     
    89 }

    6 普里姆算法(Prim)

      6.1 最小生成树(Minimum Cost Spanning Tree,MST)

      (1)给定一个带权的无向连通图,如何选取一棵生成树,使树上所有边上权的总和为最小,称为最小生成树;

      (2)N个顶点,一定有N-1条边;

      (3)包含所有顶点;

      (4)求最小生成树的算法主要是普里姆算法和克鲁斯卡尔算法;

      6.2 普里姆算法介绍

      (1)普里姆算法求最小生成树,也就是在包含n个顶点的连通图中,找出只有(n-1)条边包含所有n个顶点的连通子图,就是所谓的极小连通子图;

      (2)设G=(V,E)是连通网,T=(U,D)是最小生成树,V,U是顶点集合,E,D是边的集合;

      (3)若从顶点u开始构造最小生成树,则从集合V中取出顶点u放入到集合U中,标记顶点v 的visted[u]=1;

      (4)若集合U中顶点ui与集合V-U中顶点vj之间存在边,则寻找这些边中权值最小的边,但不能构成回路,将边(ui,vj)加入到集合D中,标记visited[vj]=1;

      (5)重复步骤3,若U与V相等,即所有顶点都被标记为访问过,此时D中有n-1条边;

      6.3 普里姆算法的应用实例

      1 package com.atguigu.prim;
      2 
      3 import java.util.Arrays;
      4 
      5 public class PrimAlgorithm {
      6 
      7     public static void main(String[] args) {
      8         // 测试图是否创建成功
      9         char[] data=new char[]{'A','B','C','D','E','F','G'};
     10         int verxs=data.length;
     11         //邻接矩阵的关系使用二维数组表示
     12         int[][] weight=new int[][] {
     13             {10000,5,7,10000,10000,10000,2},
     14             {5,10000,10000,9,10000,10000,3},
     15             {7,10000,10000,10000,8,10000,10000},
     16             {10000,9,10000,10000,10000,4,10000},
     17             {10000,10000,8,10000,10000,5,4},
     18             {10000,10000,10000,4,5,10000,6},
     19             {2,3,10000,10000,4,6,10000}
     20         };
     21         //创建MGraph对象
     22         MGraph mGraph=new MGraph(verxs);
     23         //创建MinTree对象
     24         MinTree minTree=new MinTree();
     25         minTree.createGraph(mGraph, verxs, data, weight);
     26         minTree.showGraph(mGraph);
     27         minTree.prim(mGraph, 0);
     28     }
     29 
     30 }
     31 //创建最小生成树->村庄的图
     32 class MinTree{
     33     //创建图的邻接矩阵
     34     /**
     35      * 
     36      * @param graph 图对象
     37      * @param verxs 图对应的顶点个数
     38      * @param data 图的各个顶点的值
     39      * @param weight 图的邻接矩阵
     40      */
     41     public void createGraph(MGraph graph,int verxs,char data[],int[][] weight) {
     42         int i,j;
     43         for ( i = 0; i < verxs; i++) {//顶点
     44             graph.data[i]=data[i];
     45             for ( j = 0; j < verxs; j++) {
     46                 graph.weight[i][j]=weight[i][j];
     47             }
     48         }
     49     }
     50     
     51     //显示图的邻接矩阵
     52     public void showGraph(MGraph graph) {
     53         for(int[] link:graph.weight) {
     54             System.out.println(Arrays.toString(link));
     55         }
     56     }
     57     
     58     //编写prim算法,得到最小生成树
     59     /**
     60      * 
     61      * @param graph 图
     62      * @param v 表示从图的第几个顶点开始生成
     63      */
     64     public void prim(MGraph graph,int v) {
     65         //visited[] 标记顶点是否被访问过
     66         int visited[]=new int[graph.verxs];
     67         //visitd[] 默认元素的值是0,表示没有被访问过
     68         for (int i = 0; i < visited.length; i++) {
     69             visited[i]=0;
     70         }
     71         //把当前这个顶点标记为已经访问
     72         visited[v]=1;
     73         //h1和h2为记录为两个顶点的下标
     74         int h1=-1;
     75         int h2=-1;
     76         //将minWeight初始化为一个大数,后面在遍历过程中,会被替换
     77         int minWeight=10000;
     78         //因为graph.verxs顶点,普里姆算法结束后,有graph.verxs-1边
     79         for(int k=1;k<graph.verxs;k++) {
     80             //确定顶每一次生成的子图,和哪个节点和这次遍历的距离最近
     81             for(int i=0;i<graph.verxs;i++) {//i节点表示被访问过的节点
     82                 for(int j=0;j<graph.verxs;j++) {//j节点表示还没有访问过的节点
     83                     if(visited[i]==1&&visited[j]==0&&graph.weight[i][j]<minWeight) {
     84                         minWeight=graph.weight[i][j];//替换minWeight(寻找已经访问过的节点和未访问过的节点间的权值最小的边)
     85                         h1=i;
     86                         h2=j;
     87                     }
     88                 }
     89             }//这个一条边是最小
     90             System.out.println("本次的找到边权值最小的为:"+graph.data[h1]+"===>"+graph.data[h2]+",其权值为:"+minWeight);
     91             //将当前找到的节点标记为已经访问过的
     92             visited[h2]=1;
     93             //重置minWeight
     94             minWeight=10000;
     95         }
     96     }
     97 }
     98 
     99 class MGraph{
    100     int verxs;//表示图的节点个数
    101     char[] data;//存放节点数据
    102     int[][] weight;//存放边,就是邻接矩阵
    103     public MGraph(int verxs) {
    104         this.verxs=verxs;
    105         data=new char[verxs];
    106         weight=new int[verxs][verxs];
    107     }
    108 }

    7.克鲁斯卡尔算法(Kruskal)

      7.1克鲁斯卡尔算法介绍

      (1)克鲁斯卡尔算法是用来求加权连通图的最小生成树的算法;

      (2)基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路;

      (3)具体做法:首先构造一个只含n个顶点的森林,然后依权值从小到大从连通网中选择加入到森林中,并使森林中不产生回路,直到森林变成一棵树为止;

      (4)加入的边的两个顶点不能都指向同一个终点,否则将构成回路;

      7.2 源代码

      1 package com.atguigu.Kruskal;
      2 
      3 import java.sql.Array;
      4 import java.util.Arrays;
      5 
      6 public class KruskalAlgorithm {
      7     private int edgeNum;//边的个数
      8     private char[] vertexs;//顶点数组
      9     private int[][] matrix;//邻接矩阵
     10     private static final int INF=Integer.MAX_VALUE;//使用INF表示两个质点不能连通
     11     public static void main(String[] args) {
     12         char[] vertexs= {'A','B','C','D','E','F','G'};
     13         int[][] matrix= {
     14                 {0,12,INF,INF,INF,16,14},
     15                 {12,0,10,INF,INF,7,INF},
     16                 {INF,10,0,3,5,6,INF},
     17                 {INF,INF,3,0,4,INF,INF},
     18                 {INF,INF,5,4,0,2,8},
     19                 {16,7,6,INF,2,0,9},
     20                 {14,INF,INF,INF,8,9,0}
     21         };
     22         //创建对象
     23         KruskalAlgorithm kruskalCase=new KruskalAlgorithm(vertexs, matrix);
     24         kruskalCase.print();
     25         System.out.println(Arrays.toString(kruskalCase.getEdges()));
     26         kruskalCase.kruskal();
     27 
     28     }
     29     
     30     public KruskalAlgorithm(char[] vertexs,int[][] matrix) {
     31         //初始化顶点数和边的个数
     32         int vlen=vertexs.length;
     33         
     34         //初始化顶点
     35         this.vertexs=new char[vlen];
     36         for(int i=0;i<vertexs.length;i++) {
     37             this.vertexs[i]=vertexs[i];
     38         }
     39         
     40         //初始化边,使用的复制拷贝的方式
     41         this.matrix=new int[vlen][vlen];
     42         for (int  i= 0;  i< vlen; i++) {
     43             for (int j = 0; j < vlen; j++) {
     44                 this.matrix[i][j]=matrix[i][j];
     45             }
     46         }
     47         
     48         //统计边
     49         for (int  i= 0;  i< vlen; i++) {
     50             for (int j = i+1; j < vlen; j++) {
     51                 if(this.matrix[i][j]!=INF) {
     52                     edgeNum++;
     53                 }
     54             }
     55         }
     56     }
     57     public void kruskal() {
     58         int index=0;//表示最后结果数组的索引
     59         int[] ends=new int[edgeNum];//用于保存"已有最小生成树"中的每个顶点在最小生成树中的终点
     60         //创建结果数组,保存最后的最小生成树
     61         EData[] rets=new EData[edgeNum];
     62         
     63         //获取图中所有的边的集合,一共有12边
     64         EData[] edges=getEdges();
     65         System.out.println("图的边的集合:"+Arrays.toString(edges)+"共"+edges.length);
     66         //按照边的权值大小排序
     67         sortEdge(edges);
     68         //遍历edges数组,将边添加到最小生成树时,判断准备加入的边是否形成了回路,如果没有,就加入rets,否则不能加入
     69         for (int i = 0; i < edgeNum; i++) {
     70             //获取到第i条边的第一个顶点
     71             int p1=getPosition(edges[i].start);
     72             //获取到第i条边的第二个顶点
     73             int p2=getPosition(edges[i].end);
     74             //获取到p1这个顶点在已有最小生成树中的终点
     75             int m=getEnd(ends, p1);
     76             //获取到p2这个顶点在已有最小生成树中的终点
     77             int n=getEnd(ends, p2);
     78             if(m!=n) {//说明没有构成回路
     79                 ends[m]=n;//设置m"已有最小生成树"的终点
     80                 rets[index++]=edges[i];//有一条边加入到rets数组中
     81                 
     82             }
     83         }
     84         
     85         //统计并打印最小生成树 输出rets
     86         System.out.println("最小生成树为:");
     87         for (int i = 0; i < index; i++) {
     88             System.out.println(rets[i]);
     89         }
     90     }
     91     //打印邻接矩阵
     92     public void print() {
     93         System.out.println("邻接矩阵为:
    ");
     94         for (int i = 0; i < vertexs.length; i++) {
     95             for (int j = 0; j < vertexs.length; j++) {
     96                 System.out.printf("%12d	",matrix[i][j]);
     97             }
     98             System.out.println();
     99         }
    100     }
    101     /**
    102      * 功能:先按照边的权值进行排序,冒泡排序
    103      * @param edges 边的集合
    104      */
    105     private void sortEdge(EData[] edges) {
    106         for (int i = 0; i < edges.length-1; i++) {
    107             for (int j = 0; j < edges.length-1-i; j++) {
    108                 if(edges[j].weight>edges[j+1].weight) {//交换
    109                     EData tmp=edges[j];
    110                     edges[j]=edges[j+1];
    111                     edges[j+1]=tmp;
    112                 }
    113             }
    114         }
    115     }
    116     /**
    117      * 
    118      * @param ch 顶点的值,比如'A','B'
    119      * @return 返回ch顶点对应的下标,如果找不到,返回-1
    120      */
    121     private int getPosition(char ch) {
    122         for (int i = 0; i < vertexs.length; i++) {
    123             if(vertexs[i]==ch) {
    124                 return i;
    125             }
    126         }
    127         return -1;
    128     }
    129     /**
    130      * 获取图中边,放到EData数组中,后面需要遍历该数组
    131      * 通过matrix邻接矩阵来获取
    132      * EData[] 形式['A','B',12],['B','F',7]....
    133      * @return
    134      */
    135     private EData[] getEdges() {
    136         int index=0;
    137         EData[] edges=new EData[edgeNum];
    138         for (int i = 0; i < vertexs.length; i++) {
    139             for (int j = i+1; j < vertexs.length; j++) {
    140                 if(matrix[i][j]!=INF) {
    141                     edges[index++]=new EData(vertexs[i], vertexs[j], matrix[i][j]);
    142                 }
    143             }
    144         }
    145         return edges;
    146     }
    147     /**
    148      * 获取下标为i的顶点的终点,用于后面判断两个顶点的重点是否相同
    149      * @param ends 数组就是记录了各个顶点对应的终点是哪个,ends数组在遍历过程中,逐步形成
    150      * @param i 表示传入的顶点对应的下标
    151      * @return 返回的是下标为i的这个顶点对应的终点的下标
    152      */
    153     private int getEnd(int[] ends,int i) {
    154         while(ends[i]!=0) {
    155             i=ends[i];
    156         }
    157         return i;
    158     }
    159 }
    160 
    161 //创建了一个类EData,它的对象实例就表示一条边
    162 class EData{
    163     char start;//边的一个点
    164     char end;//边的另外一个点
    165     int weight;//边的权值
    166     
    167     public EData(char start,char end,int weight) {
    168         this.start=start;
    169         this.end=end;
    170         this.weight=weight;
    171     }
    172 
    173     @Override//重写toString方法,便于输出边
    174     public String toString() {
    175         return "EData [<" + start + ", " + end + ">=" + weight + "]";
    176     }
    177 }

    8.迪杰斯特拉算法(Dijkstra)

      8.1 迪杰斯特拉算法介绍

      (1)是典型的最短路径算法,用于计算一个节点到其他节点的最短路径。它的主要特点是以起始点为中心向外层(广度优先搜索)层层扩展,直到扩展到终点为止;

      8.2 迪杰斯特拉算法过程

      设置出发顶点为v,顶点集合V(v1,v2...vi....),v到V中各顶点的距离构成距离集合Dis,Dis(d1,d2....di....),Dis集合记录着v到图中各顶点的距离(到自身可以看作0,v到vi距离为di)

      (1)从Dis中选择值最小的di并移出Dis集合,同时移出V集合中对应的顶点vi,此时v到vi即为最短路径;

      (2)更新Dis集合,更新规则为:比较v到V集合中顶点的距离值,与v通过vi到V集合中顶点的距离值,比较值较小的一个(同时也应该更新顶点的前驱节点为vi,表明是通过vi到达的);

      (3)重复执行两步骤,直到最短路径顶点为目标顶点即可结束;

      8.3 源代码

      1 package com.atguigu.dijkstra;
      2 
      3 import java.util.Arrays;
      4 
      5 public class DijkstraAlgorithm {
      6 
      7     public static void main(String[] args) {
      8         char[] vertex= {'A','B','C','D','E','F','G'};
      9         //邻接矩阵
     10         int[][] matrix=new int[vertex.length][vertex.length];
     11         final int N=65535;//表示不可连接
     12         matrix[0]=new int[] {N,5,7,N,N,N,2};
     13         matrix[1]=new int[] {5,N,N,9,N,N,3};
     14         matrix[2]=new int[] {7,N,N,N,8,N,N};
     15         matrix[3]=new int[] {N,9,N,N,N,4,N};
     16         matrix[4]=new int[] {N,N,8,N,N,5,4};
     17         matrix[5]=new int[] {N,N,N,4,5,N,6};
     18         matrix[6]=new int[] {2,3,N,N,4,6,N};
     19         //创建Graph 对象
     20         Graph graph=new Graph(vertex, matrix);
     21         graph.showGraph();
     22         //测试迪杰斯特拉算法
     23         graph.dsj(6);
     24         graph.showDijkstra();
     25     }
     26 
     27 }
     28 
     29 class Graph{
     30     private char[] vertex;//顶点数组
     31     private int[][] matrix;//邻接矩阵
     32     private VisitedVertex vv;//已经访问顶点的集合
     33     
     34     public Graph(char[] vertex,int[][] matrix) {//构造器
     35         this.vertex=vertex;
     36         this.matrix=matrix;
     37     }
     38     public void showDijkstra() {
     39         vv.show();
     40     }
     41     //显示图
     42     public void showGraph() {
     43         for(int[] link:matrix) {
     44             System.out.println(Arrays.toString(link));
     45         }
     46     }
     47     
     48     /**
     49      * 迪杰斯特拉算法实现
     50      * @param index 出发顶点对应的下标
     51      */
     52     public void dsj(int index) {
     53         vv=new VisitedVertex(vertex.length, index);
     54         update(index);//更新index下标到周围顶点的距离和前驱结点
     55         for(int j=1;j<vertex.length;j++) {
     56             index=vv.updateArr();//选择并返回新的访问顶点
     57             update(index);//更新index下标到周围顶点的距离和前驱结点
     58         }
     59     }
     60     //更新index下标顶点到周围顶点的距离和周围顶点的前驱结点
     61     public void update(int index) {
     62         int len=0;
     63         //根据遍历我们的邻接矩阵matrix[index]行
     64         for(int j=0;j<matrix[index].length;j++) {
     65             //len :出发顶点到index顶点的距离+从index顶点到j顶点的距离之和
     66             len=vv.getDis(index)+matrix[index][j];
     67             //如果j顶点没有被访问过,并且len小于出发顶点到j顶点的距离,就需要更新
     68             if(!vv.in(j)&&len<vv.getDis(j)) {
     69                 vv.update(j, index);//更新j顶点的前驱为index顶点
     70                 vv.updateDis(j, len);//更新出发顶点到j顶点的距离
     71             }
     72         }
     73     }
     74     
     75 }
     76 
     77 //已访问顶点集合
     78 class VisitedVertex{
     79     //记录各个顶点是否访问过 1表示访问过,0表示未访问过,动态更新
     80     public int[] already_arr;
     81     //每个下标对应的值为前一个顶点的下标,动态更新
     82     public int[] pre_visited;
     83     //记录出发顶点到其他所有顶点的距离,比如G为出发顶点,就会记录G到出发顶点的距离,动态更新,求的最短距离会存放在dis中
     84     public int[] dis;
     85     /**
     86      * 构造器
     87      * @param length 表示顶点的个数
     88      * @param index 出发顶点对应的下标,比如G顶点的下标为6
     89      */
     90     public VisitedVertex(int length,int index) {
     91         this.already_arr=new int[length];
     92         this.pre_visited=new int[length];
     93         this.dis=new int[length];
     94         //初始化dis数组
     95         Arrays.fill(dis, 65535);
     96         this.dis[index]=0;//设置出发顶点的访问距离为0
     97         this.already_arr[index]=1;//设置出发顶点为访问过
     98     }
     99     /**
    100      * 判断index节点是否被访问过
    101      * @param index
    102      * @return如果访问过返回true,否则返回false
    103      */
    104     public boolean in(int index) {
    105         return already_arr[index]==1;
    106     }
    107     /**
    108      * 更新出发顶点到index顶点的距离
    109      * @param index
    110      * @param len
    111      */
    112     public void updateDis(int index,int len) {
    113         dis[index]=len;
    114     }
    115     /**
    116      * 更新pre节点的前驱结点为index顶点
    117      * @param pre
    118      * @param index
    119      */
    120     public void update(int pre,int index) {
    121         pre_visited[pre]=index;
    122     }
    123     /**
    124      * 返回出发顶点到index顶点的距离
    125      * @param index
    126      */
    127     public int getDis(int index) {
    128         return dis[index];
    129     }
    130     /**
    131      * 继续选择并返回新的访问顶点,比如这里的G结束后,就是A点作为新的访问顶点
    132      * @return
    133      */
    134     public int updateArr() {
    135         int min=65535,index=0;
    136         for(int i=0;i<already_arr.length;i++) {
    137             if(already_arr[i]==0&&dis[i]<min) {
    138                 min=dis[i];
    139                 index=i;
    140             }
    141         }
    142         //更新index 顶点被访问过
    143         already_arr[index]=1;
    144         return index;
    145     }
    146     
    147     public void show() {
    148         System.out.println("==========================================");
    149         for (int i : already_arr) {
    150             System.out.print(i+" ");
    151         }
    152         System.out.println();
    153         System.out.println("==========================================");
    154         for (int i : pre_visited) {
    155             System.out.print(i+" ");
    156         }
    157         System.out.println();
    158         System.out.println("==========================================");
    159         for (int i : dis) {
    160             System.out.print(i+" ");
    161         }
    162         System.out.println();
    163         System.out.println("==========================================");
    164         char[] vertex= {'A','B','C','D','E','F','G'};
    165         int count =0;
    166         for(int i:dis) {
    167             if(i!=65535) {
    168                 System.out.print(vertex[count]+"("+i+")");
    169             }else {
    170                 System.out.println("N");
    171             }
    172             count++;
    173         }
    174         System.out.println();
    175     }
    176 }

    9.佛洛依德算法(Floyd)

      9.1 佛洛依德算法介绍

      (1)用于寻找给定的加权图中顶点间最短路径的算法;

      (2)佛洛依德算法计算图中各个顶点之间的最短路径;

      (3)迪杰斯特拉算法用于计算图中某个顶点到其他顶点的最短路径;

      (4)迪杰斯特拉算法与佛洛依德算法的比较:迪杰斯特拉算法通过选定的被访问顶点,求出出发顶点到其他顶点的最短路径;佛洛依德算法中每一个顶点都是出发访问点,所以需要将每一个顶点看作被访问顶点,求出每一个顶点到其他顶点的最短路径;

      9.2 算法分析

      (1)设置顶点vi到顶点vk的最短路径已知为Lik,顶点vk到vj的最短路径已知为Lkj,顶点vi到vj的路径为Lij,则vi到vj的最短路径为:min((Lik+Lkj),Lij),vk的取值为图中所有顶点,则可以获得vi到vj的最短路径;

      (2)vi到vk的最短路径Lik或vk到vj的最短路径Lkj,是以同样的方式获得的;

       9.3 源代码

     1 package com.atguigu.floyd;
     2 
     3 import java.util.Arrays;
     4 
     5 
     6 public class FloydAlgorithm {
     7 
     8     public static void main(String[] args) {
     9         //测试图是否创建成功
    10         char[] vertex= {'A','B','C','D','E','F','G'};
    11         //创建邻接矩阵
    12         int[][] matrix= new int[vertex.length][vertex.length];
    13         final int N=65535;
    14         matrix[0]=new int[] {0,5,7,N,N,N,2};
    15         matrix[1]=new int[] {5,0,N,9,N,N,3};
    16         matrix[2]=new int[] {7,N,0,N,8,N,N};
    17         matrix[3]=new int[] {N,9,N,0,N,4,N};
    18         matrix[4]=new int[] {N,N,8,N,0,5,4};
    19         matrix[5]=new int[] {N,N,N,4,5,0,6};
    20         matrix[6]=new int[] {2,3,N,N,4,6,0};
    21         //创建Graph对象
    22         Graph graph=new Graph(vertex.length, matrix, vertex);
    23         graph.floyd();
    24         graph.show();
    25     }
    26 
    27 }
    28 class Graph{
    29     private char[] vertex;//存放顶点的数组
    30     private int[][] dis;//保存从各个顶点出发到其他顶点的距离,最后的结果保留在该数组
    31     private int[][] pre;//保存到达目标点的前驱顶点
    32     
    33     /**
    34      * 构造器
    35      * @param length 大小
    36      * @param matrix 邻接矩阵
    37      * @param vertex 顶点数组
    38      */
    39     public Graph(int length,int[][] matrix,char[] vertex) {
    40         this.vertex=vertex;
    41         this.dis=matrix;
    42         this.pre=new int[length][length];
    43         //对pre数组初始化,注意存放的是前驱顶点的下标
    44         for (int i = 0; i < length; i++) {
    45             Arrays.fill(this.pre[i],i);
    46         }
    47     }
    48     
    49     //显示pre数组和dis数组
    50     public void show() {
    51         char[] vertex= {'A','B','C','D','E','F','G'};
    52         
    53         for (int k = 0; k < dis.length; k++) {
    54             //先将pre数组输出的一行数组
    55             for (int i = 0; i < dis.length; i++) {
    56                 System.out.print(vertex[pre[k][i]]+" ");
    57             }
    58             System.out.println();
    59             //输出dis数组的一行数据
    60             for (int i = 0; i < dis.length; i++) {
    61                 System.out.print("("+vertex[k]+"到"+vertex[i]+"的最短路径是"+dis[k][i]+")  ");
    62             }
    63             System.out.println();
    64             System.out.println();
    65         }
    66     }
    67     //佛洛依德算法
    68     public void floyd() {
    69         int len=0;//保存距离
    70         //对中间顶点的遍历,k是中间顶点的下标
    71         for (int k = 0; k < dis.length; k++) {
    72             //从i顶点开始出发['A','B',"C'...]
    73             for (int i = 0; i < dis.length; i++) {
    74                 //到j顶点结束
    75                 for (int j = 0; j < dis.length; j++) {
    76                     len=dis[i][k]+dis[k][j];//求出从i顶点出发经过k中间顶点的距离,
    77                     if(len<dis[i][j]){//如果len小于dis[i][j]
    78                         dis[i][j]=len;//更新距离
    79                         pre[i][j]=pre[k][j];//更新前驱节点
    80                     }
    81                 }
    82             }
    83         }
    84     }
    85 }

    10.回溯算法

      10.1 骑士周游问题的解决方法和思路

      (1)创建棋盘chessBoard,是一个二维数组;

      (2)将当前位置设置为已经访问,然后根据当前位置i,计算马还能走哪些位置,并放入到一个集合中(ArrayList),最多有8个位置,没走一步,step+1;

      (3)遍历ArrayList中存放的所有位置,看看哪个可以走通,走通就继续,走不通就回溯;

      (4)判断马是否完成任务,使用step和应该走的步数比较,如果没有达到数量,则表示没有完成任务,将整个棋盘置0;

      10.2 使用贪心算法对上述算法进行优化

      (1)获取到当前位置,可以走的下一个位置的集合;

      (2)需要对ps的下一步的所有的point的下一步的所有集合的个数进行非递减排序;

      1 package com.atguigu.horse;
      2 
      3 import java.awt.Point;
      4 import java.util.ArrayList;
      5 import java.util.Comparator;
      6 
      7 public class HorseChessBoard {
      8 
      9     private static int X;// 棋盘的列
     10     private static int Y;// 棋盘的行数
     11     //创建一个数组,标记棋盘的各个位置是否被访问过
     12     private static boolean visited[];
     13     //使用一个属性,标记是否棋盘的所有位置都被访问过
     14     private static boolean finished;//如果为true,表示成功
     15     public static void main(String[] args) {
     16         // 测试
     17         X=8;
     18         Y=8;
     19         int row=1;//马初始位置的行,从1开始编号
     20         int column=1;//马初始位置的列,从1开始编号
     21         //创建棋盘
     22         int[][] chessboard=new int[X][Y];
     23         visited=new boolean[X*Y];//初始值是false
     24         long start=System.currentTimeMillis();
     25         traversalChessboard(chessboard, row-1, column-1,1);
     26         long end=System.currentTimeMillis();
     27         System.out.println(end-start);
     28         
     29         //输出棋盘的最后情况
     30         for (int[] rows:chessboard) {
     31             for(int step:rows) {
     32                 System.out.print(step+"	");
     33             }
     34             System.out.println();
     35         }
     36 
     37     }
     38     /**
     39      * 完成骑士周游问题的算法
     40      * @param chessboard 棋盘
     41      * @param row 马当前位置的行 从0开始
     42      * @param column 马当前位置的列 从0开始
     43      * @param step 是第几步,初始位置就是第1步
     44      */
     45     public static void traversalChessboard(int [][] chessboard,int row,int column,int step) {
     46         chessboard[row][column]=step;
     47         visited[row*X+column]=true;//标记该位置已经被访问
     48         //获取当前位置可以走的下一个位置的集合
     49         ArrayList<Point> ps=next(new Point(column,row));
     50         //对ps进行排序,排序的规则就是对ps的所有的Point对象的下一步的位置的数目,进行非递减排序
     51         sort(ps);
     52         //遍历集合
     53         while (!ps.isEmpty())  {
     54             Point p=ps.remove(0);
     55             //如果当前的p没有被访问过
     56             if(!visited[p.y*X+p.x]) {
     57                 traversalChessboard(chessboard, p.y, p.x, step+1);
     58             }
     59         }
     60         //判断马是否完成了任务,使用step和应该走的步数进行比较;
     61         //如果没有到达数量,就表示没有完成任务
     62         if(step<X*Y && !finished) {//如果访问不成功
     63             chessboard[row][column]=0;
     64             visited[row*X+column]=false;
     65         }else {
     66             finished=true;
     67         }
     68     }
     69 
     70     /**
     71      * 根据当前位置(Point对象),计算马还能走哪些位置(Point),并放入到一个集合中(ArrayList),总共有8个位置
     72      * 
     73      * @param curPoint
     74      * @return
     75      */
     76     public static ArrayList<Point> next(Point curPoint) {
     77         // 创建一个ArrayList
     78         ArrayList<Point> ps = new ArrayList<Point>();
     79         // 创建一个Point
     80         Point p1 = new Point();
     81         // 表示马可以走5这个位置
     82         if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y - 1) >= 0) {
     83             ps.add(new Point(p1));
     84         }
     85         // 表示马可以走6这个位置
     86         if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y - 2) >= 0) {
     87             ps.add(new Point(p1));
     88         }
     89         // 表示马可以走7这个位置
     90         if ((p1.x = curPoint.x + 1) <  X && (p1.y = curPoint.y - 2) >= 0) {
     91             ps.add(new Point(p1));
     92         }
     93         // 表示马可以走0这个位置
     94         if ((p1.x = curPoint.x + 2) <  X && (p1.y = curPoint.y - 1) >= 0) {
     95             ps.add(new Point(p1));
     96         }
     97         // 表示马可以走1这个位置
     98         if ((p1.x = curPoint.x + 2) <  X && (p1.y = curPoint.y + 1) <  Y) {
     99             ps.add(new Point(p1));
    100         }
    101         // 表示马可以走2这个位置
    102         if ((p1.x = curPoint.x + 1) <  X && (p1.y = curPoint.y + 2) <  Y) {
    103             ps.add(new Point(p1));
    104         }
    105         // 表示马可以走3这个位置
    106         if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y + 2) <  Y) {
    107             ps.add(new Point(p1));
    108         }
    109         // 表示马可以走4这个位置
    110         if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y + 1) <  Y) {
    111             ps.add(new Point(p1));
    112         }
    113          return ps;
    114     }
    115     //根据当前这一步的所有的下一步的选择位置,进行非递减排序
    116     //减少回溯的可能
    117     public static void sort(ArrayList<Point> ps) {
    118         ps.sort(new Comparator<Point>() {
    119 
    120             @Override
    121             public int compare(Point o1, Point o2) {
    122                 //获取o1的下一步所有位置的个数
    123                 int count1=next(o1).size();
    124                 int count2=next(o2).size();
    125                 if(count1<count2) {
    126                     return -1;
    127                 }else if(count1==count2) {
    128                     return 0;
    129                 }else {
    130                     return 1;
    131                 }
    132             }
    133         });
    134     }
    135 }
  • 相关阅读:
    Dreamweaver中SourceAnyWhere的使用
    访问被拒绝:“AjaxPro”的解决方案
    NDoc1.3.1使用手册
    程序中操作Excel
    模拟提交程序相关专题
    利用SMTP服务发送电子邮件
    如何使用树形控件
    GDI+简单使用例子
    程序中操作Word
    签名工具使用介绍
  • 原文地址:https://www.cnblogs.com/ERFishing/p/11389789.html
Copyright © 2011-2022 走看看