递归 回溯算法经典题

迷宫问题

给一个迷宫,从起点开始找一条到终点的路径。
用二维数组来表示一个迷宫
规定1为迷宫的墙,2为走过的路径,3为不可行的路径。

public class Maze {
    //初始化迷宫
    static int[][] maze = new int[8][8];
    public static void main(String[] args) {
        //1表示墙,0表示未被踩踏,2表示走过的路径,3表示走过后发现走不通
        for (int i=0; i<8; i++){
            maze[i][0] = 1;
            maze[i][7] = 1;
        }
        for (int i=0; i<8; i++){
            maze[0][i] = 1;
            maze[7][i] = 1;
        }
        maze[3][3] = 1;
        maze[3][4] = 1;
        maze[3][5] = 1;
        maze[3][6] = 1;
    }
}

迷宫的初始图

在这里插入图片描述
选择路线并判断的方法
首先要设置起点和终点。
起点为(1,1),即第二行第二列。终点为(6,5)。
其次需要一个选择方向的顺序来进行下一步的操作,这个方法选择的是右下左上的顺时针方法。
如果终点坐标的值为2,说明走出了迷宫,递归可以结束了。
否则,判断当前传入的坐标的值
1.如果是0,说明从未涉足。可以假设当前坐标可以通行,将其设置为2.然后根据方向顺序进行递归。如果四个方向都无法通行,则将其设置为3

2.如果是1,2,3则都返回为false。

/**
     * 根据右,下,左,上的顺序来选择路线
     */
    public static boolean findWay(int i, int j){
        boolean flag = false;
        if (maze[6][5] == 2){
            return true;
        }else {
            switch (maze[i][j]){
                //如果从未被踩踏
                case 0:
                    maze[i][j] = 2;//假设可以通行
                    if (findWay(i,j+1)){//向右可以通行
                        flag = true;
                        break;
                    }else if (findWay(i+1,j)){//向下可以通行
                        flag = true;
                        break;
                    }else if (findWay(i,j-1)){//向左可以通行
                        flag = true;
                        break;
                    }else if (findWay(i-1,j)){//向上可以通行
                        flag = true;
                        break;
                    }
                    maze[i][j] = 3;//说明这点不可通行
                    return false;
                    //如果是墙
                case 1 :flag = false;
                    break;
                    //如果是走过的可行的路
                case 2 :flag = false;
                    break;
                    //如果是走过的不可行的路
                case 3 :flag = false;
                    break;
            }
            return flag;
        }
    }
findWay2(1,1);

调用此函数,参数为起点,最后递归完成时就会有一条路线。
在这里插入图片描述
如果选择的顺序方向不同,路径也会改变,比如当顺序为下上左右时,路劲就会很曲折。
在这里插入图片描述

最短路径

关于最短路径可以尝试不同的方向顺序组合,设置一个计数器记录一共走了多少步,最少的便是最短路径。

八皇后问题

在 8×8 格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法

首先要设计的当然是棋盘,但是可以用一个巧妙的数组来设计。比如:
array[7 ,3 ,0 ,2 ,5 ,1 ,6 ,4 ]
这个数组下标Q表示第Q+1个皇后,坐标是第Q+1行,第array[Q]+1列。
上面可以解读为第一个皇后放在第1行第8列,第二个皇后放在第2行第4列…

	static int max = 8;
    static int count = 0;//记录解法个数
    static int[] location = new int[max];//皇后的位置数组

设计一个方法,判断第n个皇后和前面的所有皇后是否冲突(即任意两个皇后都不能处于同一行、同一列或同一斜线上)。

/**
     * 判断第n+1个皇后是否与前面的冲突
     */
    private static boolean judge(int n){
        boolean flag = true;
        for (int i=0; i<n; i++){
            //1.两个皇后不能在同一列 2.两个皇后不能在同一斜线
            if (location[i] == location[n] || Math.abs(n-i) == Math.abs(location[n]-location[i]))
                flag = false;
        }
        return flag;
    }

这里判断的第二个条件可以理解为两个坐标之间斜率不能为1或者-1,也就是不能在同一斜线。
在这里插入图片描述
有了判断条件后就可以设置递归函数了。
先写出打印解法数组的函数。

 /**
     * 打印一种解法
     */
    private static void print(){
        for (int i = 0; i<location.length; i++){
            System.out.print(location[i]+" ");
        }
        System.out.println();
    }

递归函数
递归终止条件是放置第九个皇后的时候。
调用函数时设置形参n=0
也就是先放第一个皇后,放在第1行第1列。for循环固定执行8次循环

  • 第一次循环第一个皇后被放在了(0,0)上,显然不冲突
  • 递归开始放第二个皇后,又会进入for循环。第二个皇后被放在了(1,0)上,冲突了,则不会进行递归,而是将第二个皇后放在(1,1)上再次判断…
private static void solve(int n){
        //如果n等于8,说明放第9个皇后,此时已经解出了一种解法
        if (n == 8) {
            print();
            count++;
            return;
        }
        for(int i=0; i<max; i++){
            //把第n+1个皇后放在第i列
            location[n] = i;
            //如果不冲突,递归继续放第n+2个
            if (judge(n)){
                solve(n+1);
            }
            //如果冲突,当前的皇后向右移
        }
    }

如果成功得出了一种解法就会return,此时又会回溯到某一个节点,此节点继续向右移动开始试错。所以当回溯到第一个皇后并且执行完8次循环后,所有解法就全部打印出来了。

public static void main(String[] args) {
        solve(3);
        System.out.println(count);;
    }

一共是92种摆法。

每次递归都看的异常头疼,所以每次都要debug才能最终想通,哎

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值