【算法】骑士周游 ---递归的说明

因为说明中没有对应具体代码,请先看最下方代码在看说明

{普通递归

创建一个二维空表做棋盘

用step记录走过的步数,用来增加条件判断游戏是否成功   (棋盘上记录步数)

使用visted记录是否该点是否走过(一张一维标记对应二位棋盘是否已走过)

递归的过程回溯时将回溯前的一步step置为初始默认值0,将visted重新标记为false

【递归的解释】:按照我的个人理解进行解释,可能会比较口语并且存在不恰当的地方还请大家指出。

我要先举一个不贴切但是有助于理解的想象画面:假设有一个逻辑树状图不停的发展分支直到外部限定的条件不满足就会停止,并且分支发展下来到最末端是非常大量的,这末端就是一大堆的结果,我们要在在树状分支最开端发起一人走但可回溯的求解过程,这依赖计算机高效的数据运算完成人很难完成的事。

首先递归必须在while中进行,但是在while中递归则会进入到新状况的一个while,每一个分支的逻辑框架没有变,但是其中的值发生了改变,因为进入新的节点,所以会面临新的多种选择(也可能没有,走到了末端),直到末端,各分支链条长短不一。

遍历中进入新的遍历,一直到了末端的循环条件不满足会退出末端的循环,执行完当前所在方法循环后的游戏最终结果判定,这时需要那个外部条件来判定是否是结果达到,如果达到则直接返回,游戏结束。而没有达到则当前节点进行值的重置,变为之前状态。同时这时我们进入了末端之前节点的位置,在这个位置的集合分支中还有一些长短不一的分支可能需要一 一进入。

那我们为什么会进入回溯呢?这是因为末端循环结束后,判定了游戏是否结束,没有结束的话就不会返回,只将末端值改变为初始状态,但末端前一个的循环并未结束,while循环条件还在,也就是Arraylist还不是空,集合里的选择还没有被remove() 为空。

所以,最终我想说假如答案在所有遍历的最后一次上,那么它就等于跑完了一个骑士周游所有能走过的路。所以,遍历逻辑不变,游戏的初始在棋盘中位置会影响决定需要多长时间去碰到游戏的答案

}

{

优化:贪心算法(相对口语,看不懂请谅解)

 大家看到这个游戏初始位置有八个方法可以走,从概率学上说,这时并不知道答案,那么这八个方法得到答案的几率是相等的。

这就是贪心算法的核心,为了提高效率,更快速度增加获得答案的概率,我们可以让那些耗时较短方法先行。这八个方法在拥有其大量分支的数量上有可能是不同的,比如棋盘中5和6所拥有的下一步分支数量不同,假如5比6少,意味着5比6更快遍历回溯完其接下来的大量分支。

为什么第二节点5和6的次分支数量不同对运行速度有很大影响呢?这是因为越早的分支越能决定其后的数量规模,因为我们假定了分支链条的长度是很长的,这种长度影响很大。

假定母分支分别有2个和3个子分支,就算2个子分支分别又有10个子分支,而三个分支的子分支分别5个,

虽然20>15,但是分支链条长的话,三个子分支的要比两个分支多出一条不断分支的分支链条,因为限定条件的关系,两个子分支的的子分支不可能保持其分支持续变大或横定,来保持超过三个之分支的分支数量。

由此我们只要选择母分支中分支少的,就能更快将少分支数量的母分支遍历回溯完毕,更快增加得到答案的概率。

由此我们在代码对第一次得到的集合进行升序排序(长度从低到高),先执行短的,去找答案

}

package com.hspedu;

import java.awt.*;
import java.util.ArrayList;
import java.util.Comparator;

/**
 * @author: guorui fu
 * @versiion: 1.0
 */
public class chessBorad {

    //1.定义属性
    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();
        traversalBoard(chessBoard,row - 1,col - 1,1);
        long end = System.currentTimeMillis();
        System.out.println(end - start);
        //输出二维棋盘的当前情况
        for (int[] rows :chessBoard){
            for (int step:rows){//step 表示 该位置是马儿应该走的第几步
                System.out.print(step+ "\t");
            }
            System.out.println();
        }
    }

    //3.编写最核心的算法,遍历棋盘,如果遍历成功,就把finished 设置成 true
    //并且 将马儿走的每一步step,记录到chessBoard
    public static void traversalBoard(int[][] chessBoard,int row, int col,int step){
        //先把step 记录到 chessBoard
        chessBoard[row][col] = step;
        //将此点设置为已访问过
        visited[row * X + col] = true;
        //获取当前这个位置可以走的下一个位置有哪些
        ArrayList<Point> ps = next(new Point(col, row));
        while(!ps.isEmpty()){
            //取出一个位置(点)
            Point p = ps.remove(0);//remove后,Arraylist会重新排列,后边的往前移动
            //判断该位置是否走过,没走过递归遍历
            if (!visited[p.y * X + p.x]){
                //递归遍历
                traversalBoard(chessBoard,p.y,p.x,step + 1);
            }
        }

        //退出while  看看是否遍历成功   如果没有成功 ,就重置相应的值,然后进行回溯
        if (step < X * Y && !finished){
            chessBoard[row][col] = 0;
            visited[row * X + col] = false;
        }else{
            finished = true;
        }

    }

    //4.写一个方法 对ps的各个位置,可以走的下一个位置可走次数经进行升排序(优化)
    public static void sort(ArrayList<Point> ps){
        ps.sort(new Comparator<Point>() {
            @Override
            public int compare(Point o1, Point o2) {
                return next(o1).size() - next(o2).size();
            }
        });
    }

    //2.编写方法,可以获取当前位置,可以走的下一步的所有位置(Point 表示 x,y)
    public static ArrayList<Point> next(Point curPoint){
        //创建一个Arraylist
        ArrayList<Point> ps = new ArrayList<>();
        sort(ps);//排序,第4步完成后添加,优化
        //创建一个Point对象(点/位置),准备放入到ps
        Point p1 = new Point();

        //判断在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这个点,小于X是因为,下标从0开始
        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
        }
        //判断是否可以走1这个点
        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;
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值