数据结构与算法——Java实现递归、迷宫回溯问题、八皇后问题

目录

一、递归

1.1 介绍递归

二、迷宫回溯问题

2.1 代码实现

三、八皇后问题

3.1 基本介绍

3.2 分析思路

3.3 代码实现


一、递归

1.1 介绍递归

简单的说:递归就是方法自己调用自己,每次传入不同的变量。

递归有助于编程者解决复杂的问题,同时可以让代码变得简洁。

递归可以解决什么样的问题?

  •   八皇后问题、汉诺塔、阶乘、迷宫、球和蓝子
  •   各种算法中也会使用到递归,比如快排,归并排序,二分查找,分治算法等
  •   把用栈解决的问题编程递归代码比较简洁

递归重要的规则

  •  执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
  •  方法的局部变量是独立的,不会相互影响的
  •  如果方法中使用的是引用数据类型变量(比如数组,每一次递归都是用的同一个数据,修改的同一个数据),就会共享该引用数据类型的数据
  •   递归必须向退出递归的条件逼近,否则死循环
  •   当一个方法执行完毕,或者遇到return,就会返回。遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕。

二、迷宫回溯问题

最短路径和程序员找的策略有关

2.1 代码实现


public class MiGong {
    public static void main(String[] args) {
//       创建二维数组模拟迷宫   八行七列
        int[][] map = new int[8][7];
//       数字1代表墙  第1行和第八行全是强
        for(int i=0;i<7;i++){
            map[0][i]=1;
            map[7][i]=1;
        }
//      第一列和第七列全是墙
        for(int i=0;i<8;i++){
            map[i][0]=1;
            map[i][6]=1;
        }
//      除此之外还有挡板
        map[3][1]=1;
        map[3][2]=1;

//      模型输出一下
        for(int i=0 ;i<8 ; i++){
            for (int j=0; j<7;j++){
                System.out.print(map[i][j]+"   ");
            }
            System.out.println();
        }
        System.out.println("***********************递归回溯开始!!!!!!*********************");
//      递归回溯
        setWay(map,1,1);
//      输出小球走过的路
        for(int i=0 ;i<8 ; i++){
            for (int j=0; j<7;j++){
                System.out.print(map[i][j]+"   ");
            }
            System.out.println();
        }
    }

    /**
     * 递归开始处是11  结束处是65
     * 约定: map[i][j]=0时没有走过,1表示墙,2表示走过,是通路,3表示走过了这个路走不通
     *      如果能走到map[6][5]位置,既map[6][5],说明路能通
     * 确定策略:在走迷宫的时候按照 先走下面,走不通走右面,再走不通走上面,再走不通走左面 ,如果该点走不通再回溯
     *使用递归回溯给小球找路 找到为true
     * @param map 地图
     * @param i   从哪个位置开始找
     * @param j   从哪个位置开始找
     * @return    找到路返回true
     */
    public static  boolean setWay(int[][] map,int i ,int j){
           if(map[6][5] ==2 ){
//             到终点了
               return true;
           }else {
//              没有到终点,继续走
                if(map[i][j] ==0){
//                  先假定能走通,我们走走试试
                    map[i][j] =2;
                    if( setWay(map, i+1, j) ){  //向下走
                        return true;
                    }else if(setWay(map, i, j+1)){  //向右
                        return true;
                    }else if(setWay(map, i-1, j)) {  //向上
                        return true;
                    }else if(setWay(map, i, j-1)){   //向左
                        return true;
                    }else {
//                      运行到这里肯定是走不通
                        map[i][j] =3;
                        return false;
                    }

                }else {
//                  map[i][j] !=0  代表着可能是1,2,3,这三个都不满足我们走的条件
                    return false;
                }
           }
    }


}

三、八皇后问题

3.1 基本介绍

八皇后问题:是回溯算法的基本案例。在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后不能处于同一行、同一列或同一斜线,问有几种摆法

  答案是92种

3.2 分析思路

      首先说明:回溯的效率并不高,因为这就好像是每个路都走走试试行不行,这种方法就类似我们上学时学的穷举法,一个一个的试。比如我们这个8*8的棋盘就要执行一万五千多次,可见效率情况

解题说明:理论上应该创建一个二维数组来表示棋盘,但是实际上可以通过算法简化。用一个一维数组即可解决问题,arr[8]={0,4,7,5,2,6,1,3},其中数组下标对应第几列,数组下标对应的数值对应皇后放在哪个位置。比如arr[i]=var,var表示第i+1个皇后放在第i+1行的第val+1列

3.3 代码实现

public class Queue8 {
//  定义一个max,表示共有多少个皇后
    int max =8;  //8*8的棋盘,有8个皇后
    int[] array = new int[max];  //定义数组,存放皇后存在的位置
    int count =0;
    public static void main(String[] args) {
//        测试
        Queue8 queue8 = new Queue8();
        queue8.check(0);





    }

//   编写放置皇后的方法(n从0开始计数)
//  check每一次递归时进入到check都会有for循环,可以理解为多层for循环,这个想法太好了
    private void check(int n){
        if(n == max){
            count++;
            System.out.println(count);
            print();  //我们自己编写的,输出
//          //n=8,表示对数组来说就是第九个,很显然已经超出范围了,故我们的棋盘已经找出结果了
            return;
        }
//      依次放入皇后,并判断是否冲突
        for(int i=0;i<max;i++){
//          放置当前皇后n,放到改行的第一列
//          第n行第i列
            array[n] =i;
//          我们应该判断一下,第n行第i列放了行不行
            if (judge(n)){
//              运行到这里说明不冲突,然后再放下一个
                check(n+1);  //for循环结束或者n==max之后,就会产生回溯
            }
//          上面是不冲突的,万一冲突怎么办?
//              就会i++,又放到此行的下一个位置再次重试
        }

    }



    /**
     * 查看当我们放置第n个皇后,就去检测该皇后是否与前面已经摆放的皇后冲突
     *    这个地方我们不需要判断是否在同一行,因为我们使用了数组,数组下标代表行,故每行只能放一个
     *  n从零开始计数
     * @param n  表示第几个皇后
     * @return
     */
     private  boolean judge(int n){
        for(int i=0;i<n;i++){
            if(array[i]==array[n] ||Math.abs(n-i)==Math.abs(array[n]-array[i])){
//               array[i]==array[n]  表示在同一列
//               Math.abs(n-i)==Math.abs(array[n]-array[i])表示判断第n个皇后是否和第i皇后在同一个斜线上
//                  其实最后一个很好理解   把上面的式子变形,我们带入一下初中学的平面直角坐标系,
//                  (n-i)/(array[n]-array[i]) = -1 或1  通俗的来说就行y的变化量比上x的变化量等于正一或负一
//                  这样就是在同一个斜线上,这个地方真的有点巧妙
                return  false;
            }
        }
//      返回true说明冲突
        return true;
     }



//  写一个方法,可以将皇后摆放的位置输出
    private void print(){
        for(int i=0; i<array.length;i++){
            System.out.print(array[i]+"");
        }
        System.out.println();
    }
    
}

 

我们可以仔细的看一下下面这个图,我们看下标为0的数,是从0开始逐渐增大的,说明我们在分析阶段分析的没毛病。先把皇后在第一行的所有情况都找到,然后再把第二行的所有情况都找到,就这样依次执行到第八行,最终完成

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我爱布朗熊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值