一:普里姆算法

应用场景和问题:

(1)有胜利乡有7个村庄(A, B,C,D,E,F,G),现在需要修路把7个村庄连通

(2)各个村庄的距离用边线表示(权),比如A-B距离5公里

(3)间:如何修路保证各个村庄都能连通,并且总的修建公路总里程最短?

思路:将10条边连接即可,但是总的里程不是最小

正确思路:介绍尽可能的选择少的路线,并且每条路线最小,保证总里程数最少

最小生成树

修路问题本质就是就是最小生成树问题,先介绍一下最小生成树(Minimum CostSpanning Tree),简称MST。
(1)给定一个带权的无向连通图,如何选取一棵生成树,使树上所有边上权的总和为最小,这叫最小生成树

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

(3)包含全部顶点

(4)N-1条边都在图中

(5)举例说明(如图:)

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

普里姆算法介绍

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

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

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

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

(4)重复步骤②,直到u与v相等,即所有顶点都被标记为访问过,此时D中有n-1条边

(5)提示:单独看步骤很难理解,我们通过代码来讲解,比较好理解.

图解:

代码实现

package prim;import java.util.Arrays;
import java.util.Date;/*** 普里姆算法*/
public class PrimAlgorithm {public static void main(String[] args) {//测试看图是否创建okchar[] data = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G'};int verxs = data.length;//邻接矩阵的关系用二维数组表示,10000这个较大树,表示两个点不连通int[][] weight = new int[][]{{10000, 5, 7, 10000, 1000, 1000, 2},{5, 10000, 10000, 9, 10000, 10000, 3},{7, 10000, 10000, 10000, 8, 1000, 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 graph = new MGraph(verxs);//创建一个MinTree对象MinTree minTree = new MinTree();minTree.createGraph(graph, verxs, data, weight);//输出minTree.showGraph(graph);//测试普里姆算法minTree.prim(graph, 1);}
}//创建最小生成树=>村庄的图
class MinTree {/*** 创建图的邻接矩阵** @param graph  图对象* @param verxs  图对应的顶点个数* @param data   图的各个顶点的值* @param weight 图的邻接矩阵*/public void createGraph(MGraph graph, int verxs, char[] data, int[][] weight) {int i, j;for (i = 0; i < verxs; i++) { //顶点graph.data[i] = data[i];for (j = 0; j < verxs; 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 []标记结点(顶点)是否被访问过int[] visited = new int[graph.verxs];//visited[]默认元素值为0,表示没有访问//把当前这个结点标记为已访问visited[v] = 1;//h1和h2记录两个顶点的下标int h1 = -1;int h2 = -1;int minWeight = 10000; //将minWeight初始化成一个较大的数,后面遍历过程中,会被替换for (int k = 1; k < graph.verxs; k++) {//因为有graph.verxs顶点,普利姆算法结束后,有graph.verxs-1边//确定每一次生成的子图,和那个结点的距离最近for (int i = 0; i < graph.verxs; i++) { //i结点表示被访问过的结点for (int j = 0; j < graph.verxs; j++) { //j表示还没有访问过的结点if (visited[i] == 1 && visited[j] == 0 && graph.weight[i][j] < minWeight) {//替换minWeight(寻找已经访问过的结点和未访问过的结点间的权值最小的边)minWeight = graph.weight[i][j];h1 = i;h2 = j;}}}//找到一条边是最小System.out.println("边<" + graph.data[h1] + "," + graph.data[h2] + ">权值:" + minWeight);//将当前这个结点标记为已经访问visited[h2] = 1;//minWeight 重新设置为最大值minWeight = 10000;}}
}class MGraph {int verxs; //表示图的节点个数char[] data; //存放节点数据int[][] weight; //存放边,介绍我们的邻接矩阵public MGraph(int verxs) {this.verxs = verxs;data = new char[verxs];weight = new int[verxs][verxs];}
}

二:克鲁斯卡尔算法

应用场景-公交站问题

(1)某城市新增7个站点(A, B,C, D,E,F,G),现在需要修路把7个站点连通

(2)各站点的距离用边线表示(权),比如A-B距离12公里

(3)问:如何修路保证各个站点都能连通,并且总的修建公路总里程最短?

克鲁斯卡尔算法介绍

(1)克鲁斯卡尔(Kruskal)算法,是用来求加权连通图的最小生成树的算法。
(2)基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路
(3)具体做法:首先构造一个只含n个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止

通过示例来认识算法

在含有n个顶点的连通图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的最小生成树

举例出来的三张图代表了G4有多种的不同连接方式,说明是多样化的

那么什么时候是最小生成树呢?

就是众多连接方式中里:最小的,则成为最优的,是最小生成树

克鲁斯卡尔算法图解说明

我们以上图举例,来使用克鲁斯卡尔算法进行演示

假设:我们当前使用数组R来保存最小的生成树结果

我们发现这会造成回路,但是什么是回路?我们先来分析看看

当我们将E-F、C-D、D-E加入到数组R中时,这几条边都有了终点

关于终点的说明:

一、将所有顶点按照从小到大的顺序排列好之后;某个顶点的终点就是"与它连通的最大顶点"。

C的终点是F
D的终点是F
E的终点是F
F的终点是F

二、之前<C,E>虽然是权值最小的边,但是C和E的终点都是F,即它们的终点相同

我们加入的边的两个顶点不能都指向同一个终点,否则将构成回路。

若将<C,E>加入最小生成树的话,会形成回路。这就是判断回路的方式。

第四步:选取G4图中第六小的权值边B-F,因为它的权值为7

克鲁斯卡尔算法思路小结

根据前面的图解算法,我们能够了解到,克鲁斯卡尔算法重点需要解决的以下两个问题:

问题一:对图的所有边按照权值大小进行排序。
问题二:将边添加到最小生成树中时,怎么样判断是否形成了回路。

代码实现:

package kmp;import java.util.Arrays;/*** 克鲁斯卡尔算法*/
public class KruskalCase {private int edgeNum; //边的个数private char[] vertexs;//顶点数组private int[][] matrix;//邻接矩阵//使用INF 表示两个顶点不能联通private static final int INF = Integer.MAX_VALUE;public static void main(String[] args) {char[] vertexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};//克鲁斯卡尔算法邻接矩阵int matrix[][] = {/*A*//*B*//*C*//*D*//*E*//*F*//*G*//*A*/ {0, 12, INF, INF, INF, 16, 14},/*B*/ {12, 0, 10, INF, INF, 7, INF},/*C*/ {INF, 10, 0, 3, 5, 6, INF},/*D*/ {INF, INF, 3, 0, 4, INF, INF},/*E*/{INF, INF, 5, 4, 0, 2, 8},/*F*/ {16, 7, 6, INF, 2, 0, 9},/*G*/ {14, INF, INF, INF, 8, 9, 0},};//创建克鲁斯卡尔对象实例KruskalCase kruskalCase = new KruskalCase(vertexs, matrix);//输出构建的kruskalCase.print();kruskalCase.kruskal();}//构造器public KruskalCase(char[] vertexs, int[][] matrix) {//初始化顶点数和边的个数int vlen = vertexs.length;//初始顶点,复制拷贝的方式this.vertexs = new char[vlen];for (int i = 0; i < vertexs.length; i++) {this.vertexs[i] = vertexs[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[edgeNum];//用于保存"已有最小生成数"中的每个顶点在最小生成树中的终点//创建结果数组,保存最后的最小生成数EDate[] rets = new EDate[edgeNum];//获取图中所有集合的边,一共有12条边EDate[] edges = getEdges();System.out.println("图的边的集合" + Arrays.toString(edges) + "共" + edges.length);//按照边的权值大小进行排序(从小到大)sortEdges(edges);//遍历edges数组,将边添加到最小生成树中,判断是准备加入的边是否形成回路,如果没有,就加入rets,否则就不能加入for (int i = 0; i < edgeNum; i++) {//获取第i条边的第一个顶点(起点)int p1 = getPosition(edges[i].start);//获取到第i条边的第2给顶点int p2 = getPosition(edges[i].end);//获取p1这个顶点在已有最小生成树的终点int m = getEnd(ends, p1);//获取p2这个顶点在已有最小树中的终点int n = getEnd(ends, p2);//是否构成回路if (m != n) { //没有构成回路ends[m] = n;//设置m 在"已有最小生成树"中的终点rets[index++] = edges[i]; //有一条边加入到rets数组}}//统计并打印"最小生成树",输出retsSystem.out.println("最小生成树为");for (int i = 0; i < index; i++) {System.out.println(rets[i]);}}//打印邻接矩阵public void print() {System.out.println("邻接矩阵为:\n");for (int i = 0; i < vertexs.length; i++) {for (int j = 0; j < vertexs.length; j++) {System.out.printf("%10d\t", matrix[i][j]);}System.out.println();//换行}}/*** 对边进行排序处理,冒泡排序** @param edges 边的集合*/private void sortEdges(EDate[] 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) {//交换EDate tmp = edges[j];edges[j] = edges[j + 1];edges[j + 1] = tmp;}}}}/*** @param ch ch顶点的值,比如'A' ,'B'* @return 返回ch顶点对应的下标, 如果找不到, 返回-1*/private int getPosition(char ch) {for (int i = 0; i < vertexs.length; i++) {if (vertexs[i] == ch) { //找到return i;}}//找不到,返回-1return -1;}/*** 功能:获取图中边,放到EData[]数组中,后面我们需要遍历该数组* 是通过matrix 邻接矩阵获取* EData[] 形式[['A','B',12],['B','F',7].....]* * @return*/private EDate[] getEdges() {int index = 0;EDate[] edges = new EDate[edgeNum];for (int i = 0; i < vertexs.length; i++) {for (int j = i + 1; j < vertexs.length; j++) {if (matrix[i][j] != INF) {edges[index++] = new EDate(vertexs[i], vertexs[j], matrix[i][j]);}}}return edges;}/*** 功能:获取下标为i的顶点的终点,用于后面判断两个终点的顶点是否相同** @param ends :数组记录了各个顶点对应的终点是那个,edes,数组遍历过程中,逐步形成的* @param i    :表示传入的顶点对应的下标* @return 返回的结束下标为i的这个顶点对应的终点的下标*/private int getEnd(int[] ends, int i) {while (ends[i] != 0) {i = ends[i];}return i;}
}//创建一个类EDate ,它的对象实例就表示一条表
class EDate {char start;//边的一个点char end; //边的另一个点int weight; //边的权值//构造器public EDate(char start, char end, int weight) {this.start = start;this.end = end;this.weight = weight;}//重写toString,便于输出,输出边的信息@Overridepublic String toString() {return "EDate[" + "<" + start + ", " + end + ">=" + weight + "]";}
}

三:迪杰斯特拉算法

应用场景-最短路径问题

(1)战争时期,胜利乡有7个村庄(A,B,C,D,E,F, G),现在有六个邮差,从G点出发,需要分别把邮分别送到A, B,C,D,E,F六个村庄

(2)各个村庄的距离用边线表示(权),比如A-B距离5公里

(3)问:如何计算出G村庄到其它各个村庄的最短距离?

(4)如果从其它点出发到各个点的最短距离又是多少?

迪杰斯特拉(Dijkstra)算法介绍

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

迪杰斯特拉(Dijkstra)算法过程

设置出发顶点为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)重复执行两步骤,直到最短路径顶点为目标顶点即可结束

迪杰斯特拉算法最佳应用-最短路径


图解的方法分析思路

代码实现

package dijkstra;import java.util.Arrays;/*** 迪杰斯特拉算法 -最短路径*/
public class DijkstraAlgorithm {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 graph = new Graph(vertex, matrix);//测试graph.showGraph();//测试graph.dsj(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));}}//显示结果public void showDijkstra() {vv.show();}/*** 迪杰斯特拉算法实现** @param index 表示出发顶点对应的下标*/public void dsj(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顶点到周围顶点的距离和前驱顶点}}//更新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.in(j) && len < vv.getDis(j)) {vv.updatePre(j, index); //跟新j顶点的前驱为index顶点vv.updateDis(j, len); //更新出发顶点到j顶点的距离}}}
}//已访问项点集合
class VisitedVertex {//记录各个顶点是否访过1表示访问过,o未访问,会动态更新public int[] already_arr;//每个下标对应的值为前一个顶点下标,会动态更新public int[] pre_visited;//记录出发顶点到其他所有顶点的距离,比如G为出发顶点,就会记录G到其它顶点的距离,会动态更新,求的最短距离就会存放到dispublic 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数组Arrays.fill(dis, 65535);this.already_arr[index] = 1;//设置出发顶点被访问过this.dis[index] = 0;//设置出发顶点的访问距离为0}/*** 功能:判断index顶点是否被访问过** @param index* @return 如果访问过, 就返回true, 否则返回false*/public boolean in(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*/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("=========================");//输出already_arrfor (int i : already_arr) {System.out.print(i + " ");}System.out.println();//输出pre_visitedfor (int i : pre_visited) {System.out.print(i + " ");}System.out.println();//输出disfor (int i : dis) {System.out.print(i + " ");}System.out.println();//为了好看最后的最短距离,我们处理char[] 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.println("N");}count++;}System.out.println();}
}

四:弗洛伊德算法

弗洛伊德(Floyd)算法介绍

(1)和Dijkstra算法一样,弗洛伊德(Floyd)算法也是一种用于寻找给定的加权图中顶点间最短路径的算法。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名
(2)弗洛伊德算法(Floyd)计算图中各个顶点之间的最短路径
(3)迪杰斯特拉算法用于计算图中某一个顶点到其他顶点的最短路径。
(4)弗洛伊德算法vS迪杰斯特拉算法:迪杰斯特拉算法通过选定的被访问顶点,求出从出发访问顶点到其他顶点的最短路径;弗洛伊德算法中每一个顶点都是出发访问点,所以需要将每一个顶点看做被访问顶点,求出从每一个顶点到其他顶点的最短路径

弗洛伊德(Floyd)算法图解分析

(1)设置顶点vi到顶点vk的最短路径已知为Lik,顶点vk到vj的最短路径已知为Lkj,顶点vi到vj的路径为Lij,则vi到vj的最短路径为: min(Lik+Lkj),Lij),vk的取值为图中所有顶点,则可获得vi到vj的最短路径
(2)至于vi到k的最短路径Lik或者vk到vj的最短路径lkj,是以同样的方式获得3)弗洛伊德(Floyd)算法图解分析-举例说明

弗洛伊德算法最佳应用-最短路径

(1)胜利乡有7个村庄(A, B,C,D,E,F, G)

(2)各个村压的距离用边线表示(权),比如A-B距离5公里

(3)问:如何计算出各村庄到其它各村庄的最短距离?

代码实现

package floyd;import java.util.Arrays;/*** 弗洛伊德算法-最短路径*/
public class FloydAlgorithm {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 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'};for (int k = 0; k < dis.length; k++) {//先将pre数组输出一行for (int i = 0; i < dis.length; i++) {System.out.print(vertex[pre[k][i]] + " ");}System.out.println();//输出dis数组的一行数据for (int i = 0; i < dis.length; i++) {System.out.print("(" + vertex[k] + "到" + vertex[i] + "的最短路径是" + dis[k][i] + " )");}System.out.println();System.out.println();}}//弗洛伊德算法,容易理解,容易实现public void floyd() {int len = 0;//变量保存距离//对中间顶点遍历,k 就是中间顶点的下标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顶点for (int j = 0; j < dis.length; j++) {len = dis[i][k] + dis[k][j]; //=>从i顶点出发,经过k中间顶点,到达j顶点距离if (len < dis[i][j]) {//如果len小于dis[i][j]dis[i][j] = len;//更新距离pre[i][j] = pre[k][j];//更新前驱结点}}}}}
}

五:马踏棋盘算法

马踏棋盘算法介绍

(1)马踏棋盘算法也被称为骑士周游问题

(2)将马随机放在国际象棋的8×8棋盘Board[0~~7][0~7]的某个方格中,马按走棋规则(马走日字)进行移动。要求每个方格只进入一次,走遍棋盘上全部64个方格

马踏棋盘游戏代码实现

(1)马踏棋盘问题(骑士周游问题)实际上是图的深度优先搜索(DFS)的应用
(2)如果使用回溯(就是深度优先搜索)来解决,假如马儿踏了53个点,如图:走到了第53个,坐标(1,0),发现已经走到尽头,没办法,那就只能回退了,查看其他的路径,就在棋盘上不停的回溯…..,

思路分析

(3)分析第一种方式的问题,并使用贪心算法( greedyalgorithm)进行优化。解决马踏棋盘问题.

(4)使用前面的游戏来验证算法是否正确。

代码实现:

package horse;import java.awt.*;
import java.util.ArrayList;
import java.util.Comparator;/*** 马踏棋盘算法--深度优先*/
public class HorseChessboard {private static int X; //棋牌的列数private static int Y; //棋牌的行数//创建一个数组,标记棋牌各个位置是否被访问过private static boolean visited[];//使用一给属性,标记是否棋牌所有位置都被访问了private static boolean 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();traversalChessborad(chessboard, row - 1, column - 1, 1);long end = System.currentTimeMillis();System.out.println("共耗时" + (end - start) + "毫秒");//输出棋牌的最后情况for (int[] rows : chessboard) {for (int step : rows) {System.out.print(step + "\t");}System.out.println();}}/*** 完成骑士周游问题的算法** @param chessboard 棋牌* @param row        马儿当前的位置的行从 0开始* @param column     马儿当前位置的列 从0开始* @param step       是第几步,初始位置计算第1步*/public static void traversalChessborad(int[][] chessboard, int row, int column, int step) {chessboard[row][column] = step;visited[row * X + column] = true; //标记该位置已经访问//获取当前位置可以走的下一个位置的集合ArrayList<Point> ps = next(new Point(column, row));//对ps进行排序,排序的规则就是对ps的所有的Point对象的下一步的位置的数目,进行非递减排序sort(ps);//遍历pswhile (!ps.isEmpty()) {Point p = ps.remove(0);//取出下一个可以走的位置//判断该点是否已经访问过if (!visited[p.y * X + p.x]) {//说明还没有访问过traversalChessborad(chessboard, p.y, p.x, step + 1);}}//判断马儿是否完成任务,使用step和应该走的步数比较//如果没有达到数量,则表示没有完成任务,将整个棋牌置为0//说明:step < X * Y 成立的情况有两种//1.棋牌到目前位置,仍然没有走完//2.棋牌处于一个回溯过程if (step < X * Y && !finished) {chessboard[row][column] = 0;visited[row * X + column] = false;} else {finished = true;}}/*** 功能:根据当前位置(Point对象),计算马儿还能走那些位置(Point),并放入到一个集合中(ArrayList),最多8给位置** @param curPoint* @return*/public static ArrayList<Point> next(Point curPoint) {//创建一个ArrayListArrayList<Point> ps = new ArrayList<>();//创建一个PointPoint p1 = new Point();//表示马儿可以走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 + 2) < 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();//获取到o2的下一步的所有位置个数int count2 = next(o2).size();if (count1 < count2) {return -1;} else if (count1 == count2) {return 0;} else {return 1;}}});}
}

数据结构与算法(程序员常用十种算法下:5~10)相关推荐

  1. 数据结构与算法(程序员常用的十种算法:上1~5)

    一:二分查找 二分查找算法(非递归)介绍 (1)前面我们讲过了二分查找算法,是使用递归的方式,下面我们讲解二分查找算法的非递归方法 (2)二分查找法只适用于从有序的数列中进行查找(比如数字和字母等), ...

  2. java 二分搜索获得大于目标数的第一位_程序员常用查找算法(顺序、二分、插值、分块、斐波那契)...

    顺序查找 基本思想 属于线性查找和无序查找,从一端开始顺序扫描,直到找到与目标值value相等的元素. 这是最基本的查找方法,也是时间复杂度最高的查找算法. 在数据过多时,这种方法并不适用. 代码实现 ...

  3. Java数据结构与算法(九)-程序员常用的10中算法

    本章目录 第14章 程序员常用的10中算法 14.1 二分查找算法(非递归) 14.1.1 二分查找算法(非递归)介绍 14.2 分治算法 14.2.1 分治算法介绍 14.2.2 分治算法最佳实践- ...

  4. 第 14 章 程序员常用 10 种算法

    第 14 章 程序员常用 10 种算法 1.二分查找算法 1.1.二分查找算法介绍 前面我们讲过了二分查找算法,是使用递归的方式,下面我们讲解二分查找算法的非递归方式 二分查找法只适用于从有序的数列中 ...

  5. 程序员常用的十一种算法

    文章目录 程序员常用的十一种算法 1.二分查找算法 2.分治法 3.动态规划 4.字符串暴力匹配算法 5.KMP算法 6.贪心算法 7.普里姆算法介绍(找点) 8.克鲁斯卡尔(Kruskal)算法(找 ...

  6. 程序员面试、算法研究、编程艺术、红黑树、机器学习5大系列集锦

    (七月在线:https://www.julyedu.com/,面试 & 算法 & 机器学习在线课程) 作者:July--结构之法算法之道blog之博主. 时间:2010年10月-201 ...

  7. 程序员面试、算法研究、编程艺术、红黑树4大系列集锦与总结

    程序员面试.算法研究.编程艺术.红黑树4大经典原创系列集锦与总结 作者:July--结构之法算法之道blog之博主. 时间:2010年10月-2011年6月. 出处:http://blog.csdn. ...

  8. 程序员面试、算法研究、编程艺术、红黑树、机器学习5大经典原创系列集锦与总结

    程序员面试.算法研究.编程艺术.红黑树.机器学习5大经典原创系列集锦与总结 (七月在线:https://www.julyedu.com/,面试 & 算法 & 机器学习在线课程) 作者: ...

  9. (转载)程序员面试、算法研究、编程艺术、红黑树、机器学习5大系列集锦

    作者:July–结构之法算法之道blog之博主. 时间:2010年10月-2018年5月,一直在不断更新中.. 出处:http://blog.csdn.net/v_JULY_v . 说明:本博客中部分 ...

最新文章

  1. 机器学习之优雅落地线性回归法
  2. Swift中@IBDesignable/@IBInspectable的使用
  3. python的编程模式-Python设计模式:为了整洁又时尚的代码
  4. Eclipse安装GoClipse
  5. 2008Beta 版AJAX的一个奇怪的问题
  6. PHP常量:define和const的不同之处
  7. delphi listview1添加指定列_对表格的列进行批量处理的函数详解
  8. SparkSQL下UDF的使用
  9. 一体机的扫描至网络共享的设置
  10. 花花省淘宝客APP源码带淘宝京东拼多多唯品会优惠券自营商城本地生活CPS外卖优惠电影票话费
  11. 零基础如何学习PLC
  12. 关于H5的标签整理合集(一)
  13. 已知等价关系求商集_等价关系、商集和集合的划分.pdf
  14. linux 内网网络波动检测,Linux内网测试环境模拟网络丢包和延时
  15. Failed to load response dataNo data found for resource with given identifier
  16. 有哪些工具或者软件堪称神器?
  17. C语言数组的初始化总结
  18. MySQL优化器如何预估查询成本
  19. linux能还原文件版本吗,使用Linux文件恢复工具
  20. 微信聊天记录制作词云图

热门文章

  1. windows平台下安装ES
  2. oracle里面cat,Oracle 12c 中推荐使用 catcon.pl 执行SQL脚本
  3. 解决unrecognized relocation (0x2a) in section `.text`
  4. JAVA 基本语法 Scanner类的使用 实现步骤 三步走(Scanner)
  5. 笔记36 笨办法学python练习43面向对象OOP的文字理解(一)
  6. Halcon-图像锐化(边缘处理)
  7. 世界杯八强即将出炉,iModeluus助您知晓赛果!
  8. Fortran写nc文件nbsp;f90nbsp;netcdf
  9. 【SVN】VS2015和SVN合作
  10. 在PyCharm 中安装MongoDB库的一系列操作