当前位置: 首页 > 图灵资讯 > 技术篇> 骑士周游问题

骑士周游问题

来源:图灵教育
时间:2023-05-29 13:52:20

1.算法优化意义904

1.算法是程序的灵魂。为什么有些程序在计算海量数据时仍能保持高速计算?

2.在Unix下开发服务器程序的功能是支持数千万人同时在线。在上线之前,做内部测试。一切都可以,但在上线后,服务器无法支持它。公司的CTO优化了代码,再次上线,就像岩石一样坚固。那个

一瞬间,你就能感觉到程序有灵魂,也就是算法。

3.编程中有很多算法,如八种排序算法(冒泡、选择、插入、快速排序、回井、希尔、基数、堆排序)、搜索算法、分治算法、动态规划算法、KMP算法、贪婪算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法

4.以骑士周游为例,让朋友体验用算法优化程序的意义,让大家直观感受算法的力量

2.经典算法面试题-骑士周游9042.1马踏棋盘算法介绍及游戏演示

1.马踏棋盘算法也被称为骑士周游

2.将马随机放在国际象棋的8x8棋盘Board[0~7][0~7]的某个方格中,马按照走棋规则(马走日字)移动。每个方格只需进入一次,棋盘上有64个方格

3.游戏演示:

https://u.ali213.net/games/horsesun/index.html?gamecode=403

4.将使用图纸的遍历算法(DFS)+贪婪算法优化

骑士周游问题_马踏棋盘

2.2马踏棋盘算法介绍及游戏演示

1.马踏棋盘问题(骑士周游问题)实际上是图片深度优先搜索(DFS)的应用。

2.如果用可追溯性(即深度优先搜索)来解决,如果马踩了53分,如图:走到第53分,坐标(1、0),发现已经走到了尽头,没办法,只能回去看看其他路径,不停地在棋盘上回去。...,思维分析+代码实现

3.先用基本方法解决,再用贪婪算法(greedyalgorithm)优化。解决马踏棋盘问题,认识到不同算法对程序效率的影响

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

骑士周游问题_骑士周游问题_02

骑士周游问题_骑士周游问题_03

3.代码实现普通方法9063.1思路分析9055

解决骑士周游问题的步骤和思路分析

1.创建棋盘chesboard是一个二维数组

2.将当前位置设置为已访问位置,然后根据当前位置计算马还能走哪些位置,并将其放入集合中(ArrayList),最多8个,每走一步,使用step+1

3.遍历ArrayList中存储的所有位置,看看可以走的位置。如果可以通过,就继续。如果不能通过,可以追溯

4.判断马是否完成了任务,使用step与步骤进行比较。如果没有达到数量,则表示没有完成任务,并将整个棋盘设置为0

骑士周游问题_骑士周游问题_04

com中的代码.stulzl.horse_chessboardHorseChessBoard

package com.stulzl.horse_chessboard;import java.awt.*;import java.util.ArrayList;///马踏棋盘 906public class HorseChessBoard {    //定义属性    private static int X = 6;//表示列col是横坐标    private static int Y = 6;///表示行row是纵坐标    private static int[][] chessBoard = new int[Y][X];///两位定义数组棋盘    private static boolean[] visited = new boolean[X*Y];//记录某个位置是否通过    private static boolean finished = false;//记录马是否经历过棋盘    public static void main(String[] args) {        //测试        ///开始坐标,只要开始坐标不变,结果就不变        int row = 5;        int col = 5;        long start = System.currentTimeMillis();        traversalChessBoard(chessBoard,row-1,col-1,1);        long end = System.currentTimeMillis();        System.out.println(”遍历时间=”+(end-start));        ////输出当前棋盘情况        //增强for,解释,以二维数组为几个一维数组,即每行为一维数组,然后遍历一维数组        for(int[] rows : chessBoard){            for (int step : rows){                System.out.print(step+"\t");            }            System.out.println();        }        ///普通for/////        for (int i = 0; i < chessBoard.length; i++) {//            for (int j=0;j//                System.out.print(chessBoard[i][j]+"\t");//            }//            System.out.println();//        }    }    //编写核心算法,遍历棋盘,如果成功遍历,将finished设置为true,并且   907    ///将马走的每一步step记录在chessboard二维数组中    public static void traversalChessBoard(int[][] chessBoard,int row,int col,int step){        ///先将step记录在chesboard中        chessBoard[row][col] = step;        ///将这个位置设置为已经访问的位置,即通过,可以通过,但通过,不用再走了        visited[row*X+col] = true;        ///获得当前位置的下一个位置是什么?        ArrayList ps = next(new Point(col, row));//注意col是垂直坐标,即Y,row是横坐标,即X        //遍历        while(!ps.isEmpty(){//如果集合不是空的,则进入循环,然后取出集合中的点            //解释:取出ps集合中的第一个位置(点)(因为remove(0) 0代表当前集合的第一个元素的索引)            // ,我们在这里取出的点是使用remove删除(解释为什么边取边删除,因为我们的ps集合会边取边删除。            // 它会越来越小,这意味着删除原始的第一个元素 也就是说,索引0对应的元素,后面的第二个元素成为第一个元素,            // 就像聚在一起向前走一样,这样,我们就可以取出集合中的所有元素,因为我们不知道集合中            // 到底有多少元素,(因为下一步是否合法不确定)就不能用具体的0、1、2..等等来获取相关数据,            // 边取边删就可以了,因为删完就结束了),            // 因为进入循环的条件是判断集合是否空,当我们删除当前点的下一个位置的集合边时            // ,这一点的集合不需要进入循环判断            Point p = ps.remove(0);//从集合中取出一个点,边取边删的返回值就是这个被删点的数据            ///判断这个位置是否通过。如果没有通过,我们就让这个点递归到遍历。            //解释p.y对应row横坐标,p.x对应col纵坐标            if(!visited[p.y*X+p.x]){//如果不等于true,也就是false,false就是没有走过                //没有通过这一点,就把这一点作为当前的一点(因为我们的这一点是从集合中取出的,即“下一个合法点”,                // 进入递归,以此点为当前点) 进入递归                traversalChessBoard(chessBoard,p.y,p.x,step+1);            }        }        ///推出while循环后,看看它是否成功,如果不成功,重置相应的值,然后进行回溯        //解释!finished和true,因为我们的finished的初始值是false,finished说游戏结束了,我们将        //!finished和true,因为我们的finished的初始值是false,finished说游戏结束了,我们将        //!finished和true表示游戏还没有结束        if(step<X*Y && !finished){//表示游戏没有结束,可以进入循环            //重置            //既然走不通,就回到这一点(注意这一点不一定值起点,也可能是递归中的一个分支点)            chessBoard[row][col] = 0;            visited[row*X+col] = false;///把false放在那个时候        }else{            finished = true;///成功结束游戏        }    }    //写一个方法,可获得当前位置,可返回下一步所有合法点位置(Point表示x,y)   906    public static ArrayList next(Point curPoint){//curPoint目前的位置        //创建ArrayList,用于盛放当前坐标curPoint,下一步符合法律法规的点(最多8点)        ArrayList ps = new ArrayList<>();        如果此点坐标符合规则,///创建Point对象(点/位置)放入ps集合        Point p1 = new Point();///这个p1是curpoint下一步的合法点        ///判断curPoint是否能走到以下位置,如果能走就合法,就将该点(Point)放入到ps        ///判断能否走5个位置        if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y - 1) >= 0) {            ps.add(new Point(p1)); ////这里必须是new Point        }        ///判断能否走6个位置        if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y - 2) >= 0) {            ps.add(new Point(p1)); ////这里必须是new Point        }        ///判断能否走7个位置        if ((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y - 2) >= 0) {            ps.add(new Point(p1)); ////这里必须是new Point        }        ///判断是否可以走0位        if ((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y - 1) >= 0) {            ps.add(new Point(p1)); ////这里必须是new Point        }        ///判断能否走一个位置        if ((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y + 1) < Y) {            ps.add(new Point(p1)); ////这里必须是new Point        }        ///判断能否走2个位置        if ((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y + 2) < Y) {            ps.add(new Point(p1)); ////这里必须是new Point        }        ///判断能否走3个位置        if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y + 2) < Y) {            ps.add(new Point(p1)); ////这里必须是new Point        }        ///判断能否走4个位置        if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y + 1) < Y) {            ps.add(new Point(p1)); ////这里必须是new Point        }        return ps;    }}

4.代码实现贪心算法90884.1思路分析908

1.我们现在的下一个位置是根据我们的顺时针选择位置,所以下一个位置的数量是不确定的.

2.优化思路是:我们应该选择下一个可以少走的地方,开始走,这样可以减少回溯

3.代码:根据下一个位置的次数对我们的ps集合进行排序,并对其进行升序排序

骑士周游问题_递归_05

com中的代码.stulzl.horse_chessboard_pro908HorseChessBoardPro

package com.stulzl.horse_chessboard_pro;import java.awt.*;import java.util.ArrayList;import java.util.Comparator;///马踏棋盘 贪心算法版 908public class HorseChessBoardPro {    //定义属性    private static int X = 6;//表示列col是横坐标    private static int Y = 6;///表示行row是纵坐标    private static int[][] chessBoard = new int[Y][X];///两位定义数组棋盘    private static boolean[] visited = new boolean[X*Y];//记录某个位置是否通过    private static boolean finished = false;//记录马是否经历过棋盘    public static void main(String[] args) {        //测试        ///开始坐标,只要开始坐标不变,结果就不变        int row = 2;        int col = 2;        long start = System.currentTimeMillis();        traversalChessBoard(chessBoard,row-1,col-1,1);        long end = System.currentTimeMillis();        System.out.println(”遍历时间=”+(end-start));        ////输出当前棋盘情况        //增强for,解释,以二维数组为几个一维数组,即每行为一维数组,然后遍历一维数组        for(int[] rows : chessBoard){            for (int step : rows){                System.out.print(step+"\t");            }            System.out.println();        }        ///普通for/////        for (int i = 0; i < chessBoard.length; i++) {//            for (int j=0;j//                System.out.print(chessBoard[i][j]+"\t");//            }//            System.out.println();//        }    }    //写一个方法,对ps的每个位置,可以走下一个位置的次数进行排序, 从小到大排序可能的下一个位置  908    public static void sort(ArrayList ps) {        ps.sort(new Comparator() 别纠结{///这块            @Override            public int compare(Point o1, Point o2) {                return next(o1).size()-next(o2).size();            }        });    }    //编写核心算法,遍历棋盘,如果成功遍历,将finished设置为true,并且   907    ///将马走的每一步step记录在chessboard二维数组中    public static void traversalChessBoard(int[][] chessBoard,int row,int col,int step){        ///先将step记录在chesboard中        chessBoard[row][col] = step;        ///将这个位置设置为已经访问的位置,即通过,可以通过,但通过,不用再走了        visited[row*X+col] = true;        ///获得当前位置的下一个位置是什么?        ArrayList ps = next(new Point(col, row));//注意col是垂直坐标,即Y,row是横坐标,即X        sort(ps);//排序后  908        //遍历        while(!ps.isEmpty(){//如果集合不是空的,则进入循环,然后取出集合中的点            //解释:取出ps集合中的第一个位置(点)(因为remove(0) 0代表当前集合的第一个元素的索引)            // ,我们在这里取出的点是使用remove删除(解释为什么边取边删除,因为我们的ps集合会边取边删除。            // 它会越来越小,这意味着删除原始的第一个元素 也就是说,索引0对应的元素,后面的第二个元素成为第一个元素,            // 就像聚在一起向前走一样,这样,我们就可以取出集合中的所有元素,因为我们不知道集合中            // 到底有多少元素,(因为下一步是否合法不确定)就不能用具体的0、1、2..等等来获取相关数据,            // 边取边删就可以了,因为删完就结束了),            // 因为进入循环的条件是判断集合是否空,当我们删除当前点的下一个位置的集合边时            // ,这一点的集合不需要进入循环判断            Point p = ps.remove(0);//从集合中取出一个点,边取边删的返回值就是这个被删点的数据            ///判断这个位置是否通过。如果没有通过,我们就让这个点递归到遍历。            //解释p.y对应row横坐标,p.x对应col纵坐标            if(!visited[p.y*X+p.x]){//如果不等于true,也就是false,false就是没有走过                //没有通过这一点,就把这一点作为当前的一点(因为我们的这一点是从集合中取出的,即“下一个合法点”,                // 进入递归,以此点为当前点) 进入递归                traversalChessBoard(chessBoard,p.y,p.x,step+1);            }        }        ///推出while循环后,看看它是否成功,如果不成功,重置相应的值,然后进行回溯        //解释!finished和true,因为我们的finished的初始值是false,finished说游戏结束了,我们将        //!finished和true,因为我们的finished的初始值是false,finished说游戏结束了,我们将        //!finished和true表示游戏还没有结束        if(step<X*Y && !finished){//表示游戏没有结束,可以进入循环            //重置            //既然走不通,就回到这一点(注意这一点不一定值起点,也可能是递归中的一个分支点)            chessBoard[row][col] = 0;            visited[row*X+col] = false;///把false放在那个时候        }else{            finished = true;///成功结束游戏        }    }    //写一个方法,可获得当前位置,可返回下一步所有合法点位置(Point表示x,y)   906    public static ArrayList next(Point curPoint){//curPoint目前的位置        //创建ArrayList,用于盛放当前坐标curPoint,下一步符合法律法规的点(最多8点)        ArrayList ps = new ArrayList<>();        如果此点坐标符合规则,///创建Point对象(点/位置)放入ps集合        Point p1 = new Point();///这个p1是curpoint下一步的合法点        ///判断curPoint是否能走到以下位置,如果能走就合法,就将该点(Point)放入到ps        ///判断能否走5个位置        if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y - 1) >= 0) {            ps.add(new Point(p1)); ////这里必须是new Point        }        ///判断能否走6个位置        if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y - 2) >= 0) {            ps.add(new Point(p1)); ////这里必须是new Point        }        ///判断能否走7个位置        if ((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y - 2) >= 0) {            ps.add(new Point(p1)); ////这里必须是new Point        }        ///判断是否可以走0位        if ((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y - 1) >= 0) {            ps.add(new Point(p1)); ////这里必须是new Point        }        ///判断能否走一个位置        if ((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y + 1) < Y) {            ps.add(new Point(p1)); ////这里必须是new Point        }        ///判断能否走2个位置        if ((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y + 2) < Y) {            ps.add(new Point(p1)); ////这里必须是new Point        }        ///判断能否走3个位置        if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y + 2) < Y) {            ps.add(new Point(p1)); ////这里必须是new Point        }        ///判断能否走4个位置        if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y + 1) < Y) {            ps.add(new Point(p1)); ////这里必须是new Point        }        return ps;    }}