chapter10 十大算法
一、二分查找(非递归)
package binarysearchnorec;/*** @author : sky* @version : 1.0*/
public class BinarySearchNS {public static void main(String[] args) {int[] arr={1,3,8,10,11,67,100};int index=binarySearch(arr,8);System.out.println(index);}/*** //二分查找的非递归实现* @param arr 待查找的数组,arr是升序排序* @param target 需要查找的数* @return 返回对应下标,-1表示没有找到*/public static int binarySearch(int[] arr,int target){int left=0;int right=arr.length-1;while(left<=right){//说明可以继续查找int mid=(left+right)/2;if(arr[mid]==target){return mid;}else if(arr[mid]>target){right=mid-1;//需要向左边查找}else{left=mid+1;//需要向右边查找}}return -1;}
}
二、分治算法
package dac;/*** @author : sky* @version : 1.0*/
public class Hanoitower {public static void main(String[] args) {hanoiTower(4,'A','B','C');}//汉诺塔的移动的方法,使用分治算法public static void hanoiTower(int num,char a,char b,char c){//如果只有一个盘if(num==1){System.out.println("第1个盘 "+a+"->"+c);}else{//如果num>=2,我们总是可以看作是两个盘 1.最下面的一个盘 2.上面所有的盘//1.先把 最上面的盘A->B,移动过程会使用到c,最终目标是bhanoiTower(num-1,a,c,b);//2.把最下边的盘A->CSystem.out.println("第"+num+"个盘 "+a+"->"+c);//3.把B塔的所有盘从B->C,移动过程使用到A塔hanoiTower(num-1,b,a,c);}}
}
三、动态规划算法
0-1背包问题
package dp;/*** @author : sky* @version : 1.0*/
public class KnapsackProblem {public static void main(String[] args) {int[] weight={1,4,3};//物品的重量int[] value={1500,3000,2000};//物品的价值int capacity=4;//背包的容量int num=value.length;//物品的个数//创建二维数组,表示价值表//v[i][j] 表示在前i个物品中能够装入容量为j的背包中的最大价值int[][] v=new int[num+1][capacity+1];//记录放入的商品的情况,定义一个二维数组int[][] path=new int[num+1][capacity+1];//初始化第一行和第一列,这里体现出过程,在本程序中,可以不去处理,因为默认为0for (int i = 0; i <v.length; i++) {v[i][0]=0;//将第一列设置为0}for (int j = 0; j < v[0].length; j++) {v[0][j]=0;//将第一行设置为0}//根据得到的公式,动态规划处理for (int i = 1; i <v.length ; i++) {//不处理第一行for (int j = 1; j <v[0].length ; j++) {//不处理第一列if(weight[i-1]>j){//因为程序的i是从1开始的,原来公式中的weight[i]要修改成weight[i-1]v[i][j]=v[i-1][j];}else{//说明i从1开始,所以公式需要调整//v[i][j]=Math.max(v[i-1][j],value[i-1]+v[i-1][j-weight[i-1]]);//为了记录商品存放的情况,不能简单的使用公式需要if-else判断if(v[i-1][j] < value[i-1]+v[i-1][j-weight[i-1]]){v[i][j]=value[i-1]+v[i-1][j-weight[i-1]];//把当前的情况记录到pathpath[i][j]=1;}else{v[i][j]=v[i-1][j];}}}}//输出vfor (int i = 0; i <v.length ; i++) {for (int j = 0; j <v[i].length ; j++) {System.out.print(v[i][j]+"\t");}System.out.println();}/*System.out.println("----------------------------");//输出最后放入的是哪些商品,遍历path,这样输出会把所有情况都得到for (int i = 0; i <path.length ; i++) {for (int j = 0; j <path[i].length ; j++) {if(path[i][j]==1){System.out.printf("第%d个商品放入到背包 ",i);}}System.out.println();}*/System.out.println("----------------------------");int i=path.length-1;//行的最大下标int j=path[0].length-1;//列的最大下标while(i>0 && j>0){//从后往前遍历,从path的最后开始找if(path[i][j]==1){System.out.printf("第%d个商品放入到背包 ",i);j-=weight[i-1];//按照背包容量来说,把weight[i-1]放进去了,得往前找}i--;}//第3个商品放入到背包 第1个商品放入到背包 }
}
四、KMP算法
暴力匹配:
package kmp;/*** @author : sky* @version : 1.0*/
public class ViolenceMatch {public static void main(String[] args) {//测试暴力匹配String str1="硅硅谷 尚硅谷你尚硅 尚硅谷你尚硅谷你尚硅你好";String str2="尚硅谷你尚硅你~";int index = violenceMatch(str1, str2);System.out.println(index);}//暴力匹配算法实现public static int violenceMatch(String str1,String str2){//先把字符串转为字符数组char[] s1=str1.toCharArray();char[] s2=str2.toCharArray();int s1Len=s1.length;int s2Len=s2.length;int i=0;//i索引指向s1int j=0;//j索引指向s2while(i<s1Len && j<s2Len){//保证匹配时不越界if(s1[i]==s2[j]){//匹配成功i++;j++;}else{//匹配失败i=i-(j-1);j=0;}}//判断是否匹配成功if(j==s2Len){//匹配成功return i-j;}else{return -1;}}
}
注意:KMP算法是在移动 j 而不是在移动 i
package kmp;import java.util.Arrays;/*** @author : sky* @version : 1.0*/
public class KMPAlgorithm {public static void main(String[] args) {String str1="BBC ABCDAB ABCDABCDABDE";String str2="ABCDABD";//String str2="BBC";int[] next=kmpNext(str2);System.out.println(Arrays.toString(next));int kmp = kmp(str1, str2, next);System.out.println(kmp);}//获取到字串的部分匹配值表public static int[] kmpNext(String dest){//创建一个next数组保存部分匹配值int[] next=new int[dest.length()];next[0]=0;//如果字符串的长度为1,那么他的部分匹配值就为0for(int i=1,j=0;i<dest.length();i++){//当dest.charAt(i)!=dest.charAt(j)时,需要从next[j-1]获取新的j//直到发现dest.charAt(i)==dest.charAt(j)满足时退出//这是kmp算法的核心点while(j>0 && dest.charAt(i)!=dest.charAt(j)){j=next[j-1];}//当dest.charAt(i)==dest.charAt(j)这个条件满足时,部分匹配值就加一if(dest.charAt(i)==dest.charAt(j)){j++;}next[i]=j;}return next;}/*** //KMP搜索算法* @param str1 原字符串* @param str2 字串,匹配的字符串* @param next 部分匹配表* @return 如果是-1没有匹配到,否则,返回第一个匹配到的位置*/public static int kmp(String str1,String str2,int[] next){//遍历str1,i指向str1,j指向str2for (int i = 0,j=0; i <str1.length() ; i++) {//需要考虑str1.charAt(i)!=str2.charAt(j)的情况,需要调整j的大小//kmp算法核心点while(j>0 && str1.charAt(i)!=str2.charAt(j)){j=next[j-1];}if(str1.charAt(i)==str2.charAt(j)){j++;}if(j==str2.length()){//找到了return i-j+1;}}return -1;}
}
五、贪心算法
retainAll方法
先求两个集合的交集,然后再将求出的交集返回给hashSet1
public class Test {public static void main(String[] args) {HashSet<String> hashSet1=new HashSet<>();hashSet1.add("1");hashSet1.add("2");hashSet1.add("100");HashSet<String> hashSet2=new HashSet<>();hashSet2.add("1");hashSet2.add("2");hashSet2.add("200");hashSet1.retainAll(hashSet2);System.out.println("hashset1="+hashSet1);//hsahset1=[1, 2]}
}
贪心算法:
package greed;import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;/*** @author : sky* @version : 1.0*/
public class GreedAlgorithm {public static void main(String[] args) {//创建广播电台,放入到Map中HashMap<String, HashSet<String>> broadcasts = new HashMap<>();//将各个电台放入到broadcastsHashSet<String> hashSet1=new HashSet<>();hashSet1.add("北京");hashSet1.add("上海");hashSet1.add("天津");HashSet<String> hashSet2=new HashSet<>();hashSet2.add("广州");hashSet2.add("北京");hashSet2.add("深圳");HashSet<String> hashSet3=new HashSet<>();hashSet3.add("成都");hashSet3.add("上海");hashSet3.add("杭州");HashSet<String> hashSet4=new HashSet<>();hashSet4.add("上海");hashSet4.add("天津");HashSet<String> hashSet5=new HashSet<>();hashSet5.add("杭州");hashSet5.add("大连");//加入到mapbroadcasts.put("K1",hashSet1);broadcasts.put("K2",hashSet2);broadcasts.put("K3",hashSet3);broadcasts.put("K4",hashSet4);broadcasts.put("K5",hashSet5);//allAreas存放所有地区HashSet<String> allAreas = new HashSet<>();allAreas.add("北京");allAreas.add("上海");allAreas.add("天津");allAreas.add("广州");allAreas.add("深圳");allAreas.add("成都");allAreas.add("杭州");allAreas.add("大连");//创建一个ArrayList,存放选择的电台集合ArrayList<String> selects = new ArrayList<>();//定义一个临时集合,保存在遍历过程中存放遍历过程中的电台覆盖的地区和当前还没有覆盖地区的交集HashSet<String> tempSet = new HashSet<>();HashSet<String> tempMaxSet = new HashSet<>();//定义一个maxKey,保存在一次遍历过程中,能够覆盖最多未覆盖地区的电台的key//如果maxKey不为空,则会加入到selects中String maxKey=null;while (allAreas.size()!=0){//如果不为0,则表示还没有覆盖到所有的地区//每进行一次while循环,需要把maxKey置空maxKey=null;//遍历broadcasts,取出对应的keyfor(String key:broadcasts.keySet()){//每进行一次for,要把tempSet清空tempSet.clear();//当前这个key能够覆盖的地区HashSet<String> areas = broadcasts.get(key);tempSet.addAll(areas);//求tempSet和allAreas的交集,交集会赋给tempSettempSet.retainAll(allAreas);//这里应当是当前map中的key与allAreas求得交集应当和maxKey指向的map中的value与allAreas求得的交集比较才对。if(maxKey!=null){tempMaxSet=broadcasts.get(maxKey);tempMaxSet.retainAll(allAreas);}//tempSet.size()>broadcasts.get(maxKey).size()体现出贪心算法的特点,每次都选择最优的//如果当前集合包含未覆盖地区的数量,比maxKey指向的集合未覆盖的地区还要多,就需要重置maxKeyif (tempSet.size()>0 && (maxKey==null || tempSet.size()>tempMaxSet.size())){maxKey=key;}}//如果maxKey!=null,就应该将maxKey加入到selects中if(maxKey!=null){selects.add(maxKey);//将maxKey指向的广播电台覆盖的地区,从allAreas去掉allAreas.removeAll(broadcasts.get(maxKey));}}System.out.println("得到的选择结果是:"+selects);}
}
最小生成树
六、Prim算法
package prim;import java.util.Arrays;/*** @author : sky* @version : 1.0*/
public class Prim {public static void main(String[] args) {//测试图是否创建成功char[] data=new char[]{'A','B','C','D','E','F','G'};int vertexes=data.length;//邻接矩阵用二维数组描述,用10000较大的数表示不连通int[][] weight=new int[][]{{10000,5,7,10000,10000,10000,2},{5,10000,10000,9,10000,10000,3},{7,10000,10000,10000,8,10000,10000},{10000,9,10000,10000,10000,4,10000},{10000,10000,8,10000,10000,5,4},{10000,10000,10000,4,5,10000,6},{2,3,10000,10000,4,6,10000},};//创建一个MGraph对象MGraph mGraph=new MGraph(vertexes);//创建一个minTree对象MinTree minTree=new MinTree();minTree.createGraph(mGraph,vertexes,data,weight);//输出minTree.showGraph(mGraph);//测试primSystem.out.println("-------------------------------------");minTree.prim(mGraph,1);}
}
//创建图
class MGraph{int vertexes;//表示图的节点的个数char[] data;//存放节点数据int[][] weight;//存放边,邻接矩阵public MGraph(int vertexes) {this.vertexes = vertexes;data=new char[vertexes];weight=new int[vertexes][vertexes];}
}//创建最小生成树
class MinTree{/*** @param graph 图对象* @param vertexes 图对应的顶点个数* @param data 图中各个顶点的值* @param weight 图的邻接矩阵*/public void createGraph(MGraph graph,int vertexes,char[] data,int[][] weight){for (int i = 0; i < vertexes; i++) {//把传进来的值传给图里面的datagraph.data[i]=data[i];for (int j = 0; j < vertexes; j++) {graph.weight[i][j]=weight[i][j];}}}//显示图的邻接矩阵public void showGraph(MGraph graph){for (int [] link:graph.weight) {System.out.println(Arrays.toString(link));}}/*** //prim算法,得到最小生成树* @param graph 图* @param v 表示从图的第几个顶点开始生成 如‘a’-》0 ‘b’-》1*/public void prim(MGraph graph,int v){//visited标记节点是否被访问过,默认为0,表示没有访问过int[] visited = new int[graph.vertexes];/*for (int i = 0; i < graph.vertexes; i++) {visited[i]=0;}*///把当前节点标记为已访问visited[v]=1;//h1和h2记录两个顶点的下标int h1=-1;int h2=-1;int minWeight=10000;//将他初始成一个较大的数,后面在遍历过程中会被替换for (int k = 1; k <graph.vertexes ; k++) {//因为有graph.vertexes个顶点,prim算法结束后,有graph.vertexes-1条边//这个是确定每一次生成的子图,和哪个节点的距离最近for (int i = 0; i < graph.vertexes; i++) {//i节点表示被访问过的顶点for (int j = 0; j < graph.vertexes; j++) {//j节点表示还没有访问过的顶点if(visited[i]==1 && visited[j]==0 && graph.weight[i][j]<minWeight){//替换minWeight,寻找已经访问过的节点和未访问过的节点间权值最小的边minWeight=graph.weight[i][j];h1=i;h2=j;}}}//退出这个for循环时,找到了一条最小的边System.out.println("边<"+graph.data[h1]+","+graph.data[h2]+"> 权值:"+minWeight);//将当前找到的节点标记为已经访问visited[h2]=1;//minWeight重新设置为最大值10000minWeight=10000;}}}
七、Kruskal算法
package kruskal;import jdk.internal.org.objectweb.asm.tree.IntInsnNode;import java.util.Arrays;/*** @author : sky* @version : 1.0*/
public class Kruskal {public static void main(String[] args) {char[] vertexes={'A','B','C','D','E','F','G'};int[][] matrix={{0,12,INF,INF,INF,16,14},{12,0,10,INF,INF,7,INF},{INF,10,0,3,5,6,INF},{INF,INF,3,0,4,INF,INF},{INF,INF,5,4,0,2,8},{16,7,6,INF,2,0,9},{14,INF,INF,INF,8,9,0},};Kruskal kruskal = new Kruskal(vertexes, matrix);kruskal.print();System.out.println("-------------------------");kruskal.kruskal();}private int edgeNum;//边的个数private char[] vertexes;private int[][] matrix;//使用INF表示两个顶点不能连通private static final int INF=Integer.MAX_VALUE;//构造器public Kruskal(char[] vertexes, int[][] matrix) {//用的是复制拷贝的方式,不影响外面传进来的数组//初始化顶点数和变得个数int vlen=vertexes.length;this.vertexes=new char[vlen];for (int i = 0; i < vlen; i++) {this.vertexes[i]=vertexes[i];}//使用复制拷贝方式this.matrix=new int[vlen][vlen];for (int i = 0; i <vlen ; i++) {for (int j = 0; j < vlen; j++) {this.matrix[i][j]=matrix[i][j];}}//统计边的条数for (int i = 0; i < vlen; i++) {for (int j = i+1; j < vlen; j++) {if(this.matrix[i][j]!=INF){edgeNum++;}}}}public void kruskal(){int index=0;//表示最后结果数组的索引int[] ends=new int[vertexes.length];//用于保存已有最小生成树中的每个顶点在最小生成树中的终点//创建结果数组,保存最后的最小生成树EData[] results=new EData[vertexes.length-1];//获取原始图中所有的边的集合,一共有12条边EData[] edges=getEdges();/*System.out.println(edges.length);*///按照边的权值大小进行排序sortEdges(edges);System.out.println(Arrays.toString(edges));//遍历edges数组,将边添加到最小生成树中,并判断准备加入的边是否生成了回路,如果没有就加入到结果数组中for (int i = 0; i < edgeNum; i++) {//获取到第i条边的第一个顶点,即起点int p1=getPosition(edges[i].start);//E p1=4//获取到第i条边的第二个顶点,即终点int p2=getPosition(edges[i].end);//F p2=5//获取p1这个顶点在已有的最小生成树中的终点int m=getEnd(ends,p1);//E m=4//获取p2这个顶点在已有的最小生成树中的终点int n=getEnd(ends,p2);//F m=5//判断是否构成回路if(m!=n){//不等就没有构成回路ends[m]=n;//设置m在已有最小生成树的终点,以<E,F>为例,[0,0,0,0,5,0,0,0,0,0,0,0]//ends[n]=n; 没有必要写results[index++]=edges[i];}}//统计并打印最小生成树,输出results数组System.out.println("最小生成树:");System.out.println(Arrays.toString(results));//<E,F>,<C,D>,<D,E>,<B,F>,<E,G>,<A,B>,}//打印邻接矩阵public void print(){System.out.println("邻接矩阵为:");for (int i = 0; i <vertexes.length ; i++) {for (int j = 0; j < vertexes.length; j++) {System.out.printf("%10d\t",matrix[i][j]);}System.out.println();}}/*** //对边进行排序处理,冒泡排序* @param edges 边的集合*/private void sortEdges(EData[] edges){for (int i = 0; i <edges.length-1 ; i++) {for (int j = 0; j < edges.length-1-i; j++) {if(edges[j].weight>edges[j+1].weight){EData tmp=edges[j];edges[j]=edges[j+1];edges[j+1]=tmp;}}}}/*** 给一个顶点,返回一个下标* @param ch 顶点的值,比如 'A'* @return 返回ch顶点对应的下标,找不到返回-1*/private int getPosition(char ch){for (int i = 0; i <vertexes.length ; i++) {if(vertexes[i]==ch){return i;}}//找不到return -1;}/*** 功能:获取图中的边,放到EData[]数组中,后面我们需要遍历该数组* 通过matrix邻接矩阵来获取* EData[] 形式[['A','B',12],['B','F',7],......]* @return*/private EData[] getEdges(){int index=0;EData[] edges=new EData[edgeNum];for (int i = 0; i < vertexes.length; i++) {for (int j = i+1; j <vertexes.length ; j++) {if(matrix[i][j]!=INF){edges[index++]=new EData(vertexes[i],vertexes[j],matrix[i][j]);}}}return edges;}/*** 功能:获取下标为i的顶点的终点(动态加入的),用于后面判断两个顶点的终点是否相同* @param ends ends数组记录了各个顶点对应的终点是哪个,ends是在我们遍历加入边的过程中逐步行成的* @param i 表示传入的顶点对应的下标* @return 返回下标为i的这个顶点对应的终点的下标*/private int getEnd(int[] ends,int i){while(ends[i]!=0){i=ends[i];}return i;}
}//创建一个类,他的对象实例就表示一条边
class EData{char start;//边的一个点char end;//边的另外一个点int weight;//边的权值//构造器public EData(char start, char end, int weight) {this.start = start;this.end = end;this.weight = weight;}//重写toString,便于输出边的信息@Overridepublic String toString() {return "EData{" +"start=" + start +", end=" + end +", weight=" + weight +'}';}
}
最短路径
八、Dijkstra算法
package dijkstra;import java.util.Arrays;/*** @author : sky* @version : 1.0*/
public class Dijkstra {public static void main(String[] args) {char[] vertex={'A','B','C','D','E','F','G'};int[][] matrix=new int[vertex.length][vertex.length];final int N=65535;//表示不可链接matrix[0]=new int[]{N,5,7,N,N,N,2};matrix[1]=new int[]{5,N,N,9,N,N,3};matrix[2]=new int[]{7,N,N,N,8,N,N};matrix[3]=new int[]{N,9,N,N,N,4,N};matrix[4]=new int[]{N,N,8,N,N,5,4};matrix[5]=new int[]{N,N,N,4,5,N,6};matrix[6]=new int[]{2,3,N,N,4,6,N};//创建图对象Graph graph=new Graph(vertex,matrix);graph.showGraph();//测试dijgraph.dijkstra(2);graph.showDijkstra();}
}class Graph{private char[] vertex;//顶点数组private int[][] matrix;//邻接矩阵private VisitedVertex vv;//已经访问的顶点的集合//构造器public Graph(char[] vertex, int[][] matrix) {this.vertex = vertex;this.matrix = matrix;}//显示图public void showGraph(){for (int[] link: matrix){System.out.println(Arrays.toString(link));}}/*** //迪杰斯特拉算法实现* @param index 表示出发顶点对应的下标*/public void dijkstra(int index){vv = new VisitedVertex(vertex.length,index);update(index);//更新index顶点到周围顶点的距离和前驱顶点for (int j = 1; j < vertex.length; j++) {index=vv.updateArr();//选择并返回新的访问顶点update(index);}}//更新index下标顶点到周围顶点的距离 和 周围顶点的前驱顶点private void update(int index){int len=0;//根据遍历邻接矩阵 matrix[index]这行for (int j = 0; j < matrix[index].length; j++) {//len含义:出发顶点到index的距离+从index顶点到j的距离len=vv.getDis(index) + matrix[index][j];//如果j这个顶点没有被访问过,并且 len小于出发顶点到j顶点的距离,就需要更新if(!vv.isVisited(j) && len<vv.getDis(j)){vv.updatePre(j,index);//更新j顶点的前驱为index顶点vv.updateDis(j,len);//更新出发顶点到j顶点的距离}}}//显示结果public void showDijkstra(){vv.show();}
}class VisitedVertex{//已经访问的顶点集合public int[] already_arr;//记录各个顶点是否访问过,1表示访问过,0表示未访问,会动态更新public int[] pre_visited;//每个下标对应的值为前一个顶点下标,会动态更新public int[] dis;//记录出发顶点到其他所有顶点的距离,会动态更新/*** //构造器* @param length 表示顶点的个数* @param index 表示出发顶点对应的下标,比如G点对应6*/public VisitedVertex(int length,int index){this.already_arr=new int[length];this.pre_visited=new int[length];this.dis=new int[length];//初始化dis数组先全部填充为65535Arrays.fill(dis,65535);this.dis[index]=0;//把自己的改为0,设置出发顶点的访问距离为0this.already_arr[index]=1;}/*** 功能:判断index顶点是否被访问过* @param index* @return 如果访问过就返回true,否则返回false*/public boolean isVisited(int index){return already_arr[index]==1;}/*** 功能:更新出发顶点到index顶点的距离* @param index 哪一个顶点* @param len 长度多少*/public void updateDis(int index,int len){dis[index]=len;}/*** 功能:更新pre这个顶点的前驱顶点为index顶点* @param pre* @param index*/public void updatePre(int pre,int index){pre_visited[pre]=index;}/*** 功能:返回出发顶点到index顶点的距离* @param index 顶点下标* @return 距离*/public int getDis(int index){return dis[index];}/*** 功能:继续选择并返回新的访问节点,比如G访问完之后,就是A作为下一个顶点被访问* @return 返回下标*/public int updateArr(){int min=65535,index=0;for (int i = 0; i < already_arr.length; i++) {if(already_arr[i]==0 && dis[i]<min){min=dis[i];index=i;}}//更新index顶点被访问过already_arr[index]=1;return index;}//显示最后的结果,即将三个数组的结果输出public void show(){System.out.println("----------------------");System.out.println(Arrays.toString(already_arr));System.out.println(Arrays.toString(pre_visited));//输出dischar[] vertex={'A','B','C','D','E','F','G'};int count=0;for(int i:dis){if(i!=65535){System.out.print(vertex[count]+"("+i+") ");}else{System.out.print("N");}count++;}System.out.println();//[1, 1, 1, 1, 1, 1, 1]//[6, 6, 0, 5, 6, 6, 0]//A(2) B(3) C(9) D(10) E(4) F(6) G(0)}
}
九、Floyd算法
三重for循环
package floyd;import java.util.Arrays;/*** @author : sky* @version : 1.0*/
public class Floyd {public static void main(String[] args) {//测试图char[] vertex={'A','B','C','D','E','F','G'};int[][] matrix=new int[vertex.length][vertex.length];final int N=65535;//表示不可链接matrix[0]=new int[]{0,5,7,N,N,N,2};matrix[1]=new int[]{5,0,N,9,N,N,3};matrix[2]=new int[]{7,N,0,N,8,N,N};matrix[3]=new int[]{N,9,N,0,N,4,N};matrix[4]=new int[]{N,N,8,N,0,5,4};matrix[5]=new int[]{N,N,N,4,5,0,6};matrix[6]=new int[]{2,3,N,N,4,6,0};//创建图对象Graph graph = new Graph(vertex.length, matrix, vertex);graph.floyd();graph.show();}
}class Graph{private char[] vertex;//存放顶点数组private int[][] dis;//保存从各个顶点出发到其他顶点的距离,最后的结果也保留在该数组中 动态变化的private int[][] pre;//保存到达目标顶点的前驱顶点,动态变化的/*** //构造器* @param length 长度大小* @param matrix 邻接矩阵* @param vertex 顶点数组*/public Graph(int length,int[][] matrix,char[] vertex){this.vertex=vertex;this.dis=matrix;this.pre=new int[length][length];//对pre数组初始化,存放的是前驱顶点的下标for (int i = 0; i < length; i++) {Arrays.fill(pre[i],i);//初始化前驱就是自己}}//显示pre数组和dis数组public void show(){char[] vertex={'A','B','C','D','E','F','G'};System.out.println("距离数组:");for (int i = 0; i <dis.length ; i++) {//将dis数组输出for (int j = 0; j <dis.length ; j++) {System.out.print("("+vertex[i]+"到"+vertex[j]+"的最短路径是 "+dis[i][j]+") ");}System.out.println();}System.out.println("------------------------------------------------------");System.out.println("前驱节点数组:");for (int i = 0; i <pre.length ; i++) {//先将pre数组输出for (int j = 0; j <pre.length ; j++) {System.out.print(vertex[pre[i][j]]+" ");}System.out.println();}}//弗洛伊德算法public void floyd(){int len=0;//用来保存距离//从中间顶点的遍历,k就是中间顶点的下标{'A','B','C','D','E','F','G'};for (int k = 0; k < dis.length; k++) {//从i顶点开始出发,也就是出发顶点{'A','B','C','D','E','F','G'};for (int i = 0; i < dis.length; i++) {//j代表终点,{'A','B','C','D','E','F','G'};for (int j = 0; j < dis.length; j++) {len=dis[i][k]+dis[k][j];//相当于求出从i顶点出发,经过k中间顶点,到达j顶点的距离if(len<dis[i][j]){dis[i][j]=len;//更新距离pre[i][j]=pre[k][j];//更新前驱顶点,终点的前驱}}}}}
}
十、马踏棋盘算法、骑士周游算法
原始算法:太慢了!
package horse;import java.awt.*;
import java.util.ArrayList;/*** @author : sky* @version : 1.0*/
public class HorseChessboard {private static int X;//棋盘的列数private static int Y;//棋盘的行数//创建一个数组,标记棋盘的各个位置是否被访问过private static boolean[] visited;//使用一个属性,标记是否棋盘的所有位置都被访问private static boolean finished;//如果为true,表示成功/*** //根据当前位置,计算马儿还能走哪些位置,并放入到一个集合中,最多有8个位置* Point java中的一个类,表示点* @param curPoint 当前的点* @return 返回一个集合*/public static ArrayList<Point> next(Point curPoint){//创建一个ArrayListArrayList<Point> ps = new ArrayList<>();//创建一个PointPoint p1 = new Point();//x是列,y是行//表示马儿可以走5那个点if((p1.x=curPoint.x-2)>=0 && (p1.y=curPoint.y-1)>=0){ps.add(new Point(p1));}//判断马儿能不能走6位置if((p1.x=curPoint.x-1)>=0 && (p1.y=curPoint.y-2)>=0){ps.add(new Point(p1));}//判断马儿能不能走7位置if((p1.x=curPoint.x+1) <X && (p1.y=curPoint.y-2)>=0){ps.add(new Point(p1));}//判断马儿能不能走0位置if((p1.x=curPoint.x+2) <X && (p1.y=curPoint.y-1)>=0){ps.add(new Point(p1));}//判断马儿能不能走1位置if((p1.x=curPoint.x+2) <X && (p1.y=curPoint.y+1)<Y){ps.add(new Point(p1));}//判断马儿能不能走2位置if((p1.x=curPoint.x+1) <X && (p1.y=curPoint.y+2)<Y){ps.add(new Point(p1));}//判断马儿能不能走3位置if((p1.x=curPoint.x-1)>=0 && (p1.y=curPoint.y+2)<Y){ps.add(new Point(p1));}//判断马儿能不能走4位置if((p1.x=curPoint.x-2)>=0 && (p1.y=curPoint.y+1)<Y){ps.add(new Point(p1));}return ps;}/*** 完成骑士周游问题的算法* @param chessboard 棋盘* @param row 代表马儿当前位置的行,从0开始* @param column 代表马儿当前位置的列,从0开始* @param step 表示马儿走第几步,初始位置就是第一步*/public static void travelChessboard(int[][] chessboard,int row,int column,int step){chessboard[row][column]=step;//4*8+4=36,因为visited是一个一维数组visited[row*X+column]=true;//标记该位置已经访问//获取当前位置可以走的下一个位置的集合ArrayList<Point> next = next(new Point(column, row));//x是列,y是行,要对应//遍历ArrayListwhile(!next.isEmpty()){Point p = next.remove(0);//取出一个可以走的位置//判断该点是否已经访问过if(!visited[p.y*X+p.x]){//说明还没有访问过travelChessboard(chessboard,p.y,p.x,step+1);}}//判断马儿是否完成了任务,使用step的应该走的步数比较,如果没有,则表示没有完成任务,将整个棋盘置0if(step<X*Y && !finished){//step<X*Y成立的情况有两种://1.棋盘到目前位置仍然没有走完//2.棋盘处于一个回溯过程chessboard[row][column]=0;visited[row*X+column]=false;}else{finished=true;}}public static void main(String[] args) {System.out.println("骑士周游!");//测试骑士周游算法是否正确X=8;Y=8;int row=1;//马儿初始位置的行,从1开始编号int column=1;//马儿初始位置的列,从1开始编号//创建棋盘int[][] chessboard=new int[X][Y];visited=new boolean[X*Y];//初始值都是false//测试耗时long start = System.currentTimeMillis();travelChessboard(chessboard,row-1,column-1,1);long end = System.currentTimeMillis();System.out.println("耗时:"+(end-start));//22014ms//输出棋盘的情况for (int[] rows:chessboard){for (int columns:rows){System.out.print(columns+"\t");}System.out.println();}}
}
使用贪心算法优化:
package horse;import java.awt.*;
import java.util.ArrayList;
import java.util.Comparator;/*** @author : sky* @version : 1.0*/
public class HorseChessboard {private static int X;//棋盘的列数private static int Y;//棋盘的行数//创建一个数组,标记棋盘的各个位置是否被访问过private static boolean[] visited;//使用一个属性,标记是否棋盘的所有位置都被访问private static boolean finished;//如果为true,表示成功/*** //根据当前位置,计算马儿还能走哪些位置,并放入到一个集合中,最多有8个位置* Point java中的一个类,表示点* @param curPoint 当前的点* @return 返回一个集合*/public static ArrayList<Point> next(Point curPoint){//创建一个ArrayListArrayList<Point> ps = new ArrayList<>();//创建一个PointPoint p1 = new Point();//x是列,y是行//表示马儿可以走5那个点if((p1.x=curPoint.x-2)>=0 && (p1.y=curPoint.y-1)>=0){ps.add(new Point(p1));}//判断马儿能不能走6位置if((p1.x=curPoint.x-1)>=0 && (p1.y=curPoint.y-2)>=0){ps.add(new Point(p1));}//判断马儿能不能走7位置if((p1.x=curPoint.x+1) <X && (p1.y=curPoint.y-2)>=0){ps.add(new Point(p1));}//判断马儿能不能走0位置if((p1.x=curPoint.x+2) <X && (p1.y=curPoint.y-1)>=0){ps.add(new Point(p1));}//判断马儿能不能走1位置if((p1.x=curPoint.x+2) <X && (p1.y=curPoint.y+1)<Y){ps.add(new Point(p1));}//判断马儿能不能走2位置if((p1.x=curPoint.x+1) <X && (p1.y=curPoint.y+2)<Y){ps.add(new Point(p1));}//判断马儿能不能走3位置if((p1.x=curPoint.x-1)>=0 && (p1.y=curPoint.y+2)<Y){ps.add(new Point(p1));}//判断马儿能不能走4位置if((p1.x=curPoint.x-2)>=0 && (p1.y=curPoint.y+1)<Y){ps.add(new Point(p1));}return ps;}//根据当前这一步所有的下一步的选择位置进行非递减排序,减少回溯的次数public static void sort(ArrayList<Point> ps){ps.sort(new Comparator<Point>() {@Overridepublic int compare(Point o1, Point o2) {//先获取到o1下一步的所有位置的个数int count1 = next(o1).size();//o2int count2 = next(o2).size();if(count1<count2){return -1;}else if(count1==count2){return 0;}else{return 1;}}});}/*** 完成骑士周游问题的算法* @param chessboard 棋盘* @param row 代表马儿当前位置的行,从0开始* @param column 代表马儿当前位置的列,从0开始* @param step 表示马儿走第几步,初始位置就是第一步*/public static void travelChessboard(int[][] chessboard,int row,int column,int step){chessboard[row][column]=step;//4*8+4=36,因为visited是一个一维数组visited[row*X+column]=true;//标记该位置已经访问//获取当前位置可以走的下一个位置的集合ArrayList<Point> next = next(new Point(column, row));//x是列,y是行,要对应//对next进行排序,排序的规则集合中所有元素下一步位置的个数进行非递减排序sort(next);//遍历ArrayListwhile(!next.isEmpty()){Point p = next.remove(0);//取出一个可以走的位置//判断该点是否已经访问过if(!visited[p.y*X+p.x]){//说明还没有访问过travelChessboard(chessboard,p.y,p.x,step+1);}}//判断马儿是否完成了任务,使用step的应该走的步数比较,如果没有,则表示没有完成任务,将整个棋盘置0if(step<X*Y && !finished){//step<X*Y成立的情况有两种://1.棋盘到目前位置仍然没有走完//2.棋盘处于一个回溯过程chessboard[row][column]=0;visited[row*X+column]=false;}else{finished=true;}}public static void main(String[] args) {System.out.println("骑士周游!");//测试骑士周游算法是否正确X=8;Y=8;int row=1;//马儿初始位置的行,从1开始编号int column=1;//马儿初始位置的列,从1开始编号//创建棋盘int[][] chessboard=new int[X][Y];visited=new boolean[X*Y];//初始值都是false//测试耗时long start = System.currentTimeMillis();travelChessboard(chessboard,row-1,column-1,1);long end = System.currentTimeMillis();System.out.println("耗时:"+(end-start));//20ms//输出棋盘的情况for (int[] rows:chessboard){for (int columns:rows){System.out.print(columns+"\t");}System.out.println();}}
}
chapter10 十大算法相关推荐
- 十大算法,描述+代码+演示+分析+改进(赶紧收藏!)
十大算法 1.冒泡排序 (1)算法描述 冒泡排序是一种简单的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来.走访数列的工作是重复地进行直到没有再需要 ...
- java培训:Java的十大算法
想要学好java语言,就要打好基础,java要学习的东西有很多,今天小编就来和大家说下java的十大算法. 算法一:快速排序算法 快速排序是由东尼·霍尔所发展的一种排序算法.在平均状况下,排序 n 个 ...
- 细数二十世纪最伟大的十大算法
参考文献: The Best of the 20th Century: Editors Name Top 10 Algorithms. By Barry A. Cipra.地址:http://www. ...
- 新手入门机器学习十大算法
新手入门机器学习十大算法 2018年9月17日 磐石 TensorFlowNews, 机器学习 0 在机器学习的世界中,有一种被称为"无免费午餐"的定理. 它意在说明没有哪种算法能 ...
- 统治世界的十大算法(转)
原文:http://www.ctocio.com/ccnews/15714.html 软件正在吃掉世界,而软件的核心则是算法.算法千千万万,又有哪些算法属于"皇冠上的珍珠"呢?Ma ...
- 数据挖掘经典十大算法_对基本概念的理解
数据挖掘经典十大算法 一.十大经典算法 二.信息量 信息量是对信息的度量,例如时间的度量是秒,我们考虑一个离散的随机变量x时,当我们观察到的这个变量的一个具体值的时候,我们接收到的多少信息 用信息量来 ...
- 人工智能领域的十大算法
事实上,人工智能已经存在于我们生活中很久了.但对很多人来讲,人工智能还是一个较为"高深"的技术,然而再高深的技术,也是从基础原理开始的.人工智能领域中就流传着10大算法,它们的原理 ...
- 数学建模_数学模型的分类数学建模十大算法
数学模型的分类 数学建模十大算法 1.蒙特卡罗算法 (该算法又称随机性模拟算法, 是通过计算机仿真来解决问题的算法, 同时可以 通过模拟可以来检验自己模型的正确性,比较好用的算法) 2.数据拟合.参数 ...
- 二十世纪最伟大的十大算法
发明十大算法的其中几位算法大师 一.1946 蒙特卡洛方法 [1946: John von Neumann, Stan Ulam, and Nick Metropolis, all at the Lo ...
最新文章
- C#用Tesseract进行OCR识别,可识别中英日韩所有语言
- 语音识别可以直接编码吗
- python多线程加速for循环_多线程-如何在Python的循环中对操作进行多线程
- Python之tkinter:动态演示调用python库的tkinter带你进入GUI世界(Canvas)
- Gold Balanced Lineup - poj 3274 (hash)
- Spring.NET学习笔记10——方法的注入(基础篇) Level 200
- 20151227感知机(perceptron)
- echarts vue 柱状图实例_「源码学习」适用于 Vue3 的 ECharts 包装组件
- Django中Settings中Templates的路径设置
- SQL Server字符串处理函数大全
- 加热垫美国站UL130测试项目及周期
- [SUCTF 2018]GetShell
- 2019《中国智慧城市建设产业全景图谱》
- Sketch的下载与安装
- html5文字云在线制作,一键生成高大上的文字云,这5个工具值得推荐。
- python发邮件被认定为垃圾邮件_Python:脚本发送的邮件被Gmail标记为垃圾邮件
- DNX451 与 DNXCORE50 获取当前应用程序目录
- wp:涅普冬令营(2021) 监听消息
- uni-app引用阿里巴巴官方图标库
- 【51 Nod 1326】遥远的旅途
热门文章
- 【C#】简繁体转换类
- mysql约束简单理解
- 【转载】sdcard中平添文件总是提示Failed to push the item(s)Failed to push XXXXX.txt on emulato...
- 艾美捷双链RNA定量试剂盒作用盒原理分析
- 第十二届蓝桥杯嵌入式——赛后总结
- 基于大连理工大学的情感词汇表的中文情感分析
- Win Server2003常见问题及解决然方案(转)
- IBM ServerGuide 9.0
- 给地震监视器添加Notification
- 一键实现Windows和MacOS同屏操作,是什么神级体验?