递归解决八皇后问题 Java语言

      八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

在这里,我们将会使用二维数组和递归的方法来解决这个复杂的问题。

    第一种方法是通过复制最开始的棋盘arr(开始的棋盘是一个二维数组,当然初始化的时候里面默认都为零,我们用1来表示存在皇后)到newarr,然后在newarr棋盘上放置皇后。我们先从第一行的第一列开始存放皇后

      接下来,我们判断第二个皇后存放在第二行的第危险,这样会发现正上方以经存在了皇后,所以我们继续判断第二行的第二列是否危险,这样又会发现在对角线的左上方还是存在皇后,我们会继续执行上次的操作,将继续判断第二行的第三列,直到在第二行存放的皇后没有“危险”,然后将皇后放入棋盘对应的位置;当第二行也存放好了,接下来就执行eightqueen(newarr,row+1)函数,即程序走向下一行继续判断然后继续放入皇后,如果按照这种操作一直执行到最后一行,并且最后一行中都不可以存放皇后时,我们就要进行回溯

(最后一行是“危险的”)

       此时,我们需要做的就是回溯到上一层,然后将上一层存放的皇后拿掉,接着在上一层再找到存放的合适的位置,如果在上一层还是没有找到,那么就将继续回溯,直到满足条件为止。

上面的图是从第二张图开始回溯得来的,在当前的情况下都没有任何危险,然后就可以接着向下递归了,如果到后面继续存在之前的情况,那我们就进行回溯,然后再递归,直到有一种结果出来为止。

class EightQueen{
    public static int count = 0;    //创建计数器
    public static int[][] arr = new int[8][8];    //创建开始的棋盘数组
    
    public static void main(String[] args){
        eightqueen(arr,0);    //开始执行递归程序
    }

    public static void eightqueen(int[][] arr,int row){    //将原来的数组和刚开始的行数传进来
        int[][] newarr = new int[8][8];    //创建一个新的8*8的数组
        for(int i=0;i<arr.length;i++){    //将之前的arr数组的值赋给newarr
            for(int j=0;j<arr[i].length;j++){
                newarr[i][j] = arr[i][j];
            }
        }
        if(row == 8){    //递归的终止条件,当row走到第八行的时候说明一种可能以经完成
            count++;    //记录有多少种情况
            show(newarr);    //打印当前的这种情况
        }
        for(int j=0;j<newarr.length;j++){    //遍历新的数组
            if(noDangerous(newarr,row,j)){    //判断当前列和对角线上是否有“危险”
                for(int c=0;c<newarr.length;c++){    //存放皇后前先将当前行清空(回溯的时候也会将上层清空)
                    newarr[row][c] = 0;    
                }
                newarr[row][j] = 1;    //清空后然后进行皇后的存放
                eightqueen(newarr,row+1);    //进行函数递归,将新的存放后的素组继续传给下一个函数,然后行数加一
            }
        }
    }

    public static void show(int[][] arr){    //打印当前的棋盘
        System.out.println("第"+count+"次结果");
        for(int i=0;i<arr.length;i++){
            for(int j=0;j<arr[i].length;j++){
                System.out.print(arr[i][j]+" ");
            }
            System.out.println();
        }
    }

    public static boolean noDangerous(int[][] newarr,int row,int j){    //判断目前存放的皇后是否存在“危险”,下面的方法很好理解,大家认真阅读就可以了
        for(int i=row-1;i>=0;i--){
            if(newarr[i][j]==1){
                return false;
            }
        }
        for(int x=row-1,y=j-1;x>=0&&y>=0;x--,y--){
            if(newarr[x][y]==1){
                return false;
            }
        }
        for(int x=row-1,y=j+1;x>=0&&y<newarr.length;x--,y++){
            if(newarr[x][y]==1){
                return false;
            }
        }
        return true;
    }

上述是第一种方法,下面是第二种方法,通过一个二维数组来解决问题,其实两者使用的方法都一样,都是递归和回溯。

class EightQueen1{
    public static int[][] arr = new int[8][8];
    public static int count = 0;
    public static void main(String[] args){
        eightqueen(0);
    }
    public static void show(){    //在打印结果的时候只打印8*8的范围就可以了
        System.out.println("第"+count+"次结果:");
        for(int i=0;i<arr.length;i++){
            for(int j=0;j<arr[i].length;j++){
                System.out.print(arr[i][j]+" ");
            }
            System.out.println();
        }
    }   
     public static void eightqueen(int row){    //将行数传进来
        if(row == 8){    //当它执行到第八行的时候结束递归
            count++;
            show();
            return ;
        }
        for(int col=0;col<arr.length;col++){
            if(noDangerous(row,col)){    //判断是否“危险”
                arr[row][col] = 1;    //通过判断,然后将皇后存放进去
                eightqueen(row+1);    //开始向下一行进行递归
            }
            //回溯,将上一层的皇后置为零
            arr[row][col] = 0;
        }
    }
    public static boolean noDangerous(int row,int col){    //判断函数
        for(int i=row-1;i>=0;i--){
            if(arr[i][col]==1){
                return false;
            }
        }
        for(int i=row-1,j=col-1;i>=0&&j>=0;i--,j--){
            if(arr[i][j]==1){
                return false;
            }
        }
        for(int i=row-1,j=col+1;i>=0&&j<arr.length;i--,j++){
            if(arr[i][j]==1){
                return false;
            }
        }
        return true;
    }

上面的两个方法其实思路都是一样的,在判断危险这个函数中,我们其实可以运用更为简单的方法,利用绝对值来进行简化代码。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值