通过一维数组、二维数组实现n皇后问题(Java)

1.二维数组实现n皇后问题

n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。不能相互攻击指的是任意两个皇后都不可以在同一行、在同一列、在同一斜线上

思路:
1、准备工作:新建一个 nxn 的二维数组,表示棋盘,如果二维数组的值为0,就表示没有摆放皇后,为1说明了摆放了皇后;定义一个变量count,表示摆放方法
2、将第一个皇后放在第一行的第一列,这必然满足没有相互攻击,因为只有一个皇后嘛。
3、开始摆放第二个皇后(第二个皇后放在第二行),同样的也是从第一列开始摆放,然后同之前的皇后作比较,判断是否在同一行、同一列、同一斜线上,如果是,那么就将第二个皇后下移一个位置,直到两者不能相互攻击,这才可继续摆放第三个皇后。
4、当第二个皇后摆放完毕之后,那么开始摆放第三个皇后(第三个皇后放在第三行),它也是先从第一列开始摆放(由此可见,第几个皇后就放在第几行,然后每一个皇后一开始都是从第一列开始摆放的,然后在和之前的皇后作比较,判断是否能够相互攻击,然后再确定其位置),然后同之前的皇后作比较,判断是否在同一行、同一列、同一斜线上,如果是,那么就将第三个皇后后移一个位置,直到第三个皇后和前两个皇后不在相互攻击,这才可以摆放第四个皇后。
5、重复上面的步骤直到二维数组第一行的所有情况都考虑过了(就是遍历完了第一行的所有列)。

判断是否能够相互攻击:只要看任意两个皇后是否在同一列、同一行、同一条斜线上
在这里插入图片描述

代码:

/**
     * 判断是否能够相互攻击----任意两个皇后都不可以在同一列、同一行、同一条斜线上
     * @param low      当前皇后所在位置的行
     * @param high     当前皇后所在位置的列
     * @return          如果返回的是true,表示当前的位置可以摆放皇后,否则返回false,说明当前位置不可以摆放皇后
     */
    public static boolean judge(int low, int high){
        //判断当前位置所放的皇后是否和前面所放的皇后是在同一列,如果是,就返回false,表示不可以在这个位置摆放皇后
        for(int i = low - 1; i>=0; i--){
            if(arr[i][high] == 1)
                return false;
        }
//        //判断当前位置所放的皇后是否和前面所放的皇后是在同一行,如果是,就返回false,表示不可以在这个位置摆放皇后
//        for(int i = high - 1; i>=0; i--){
//            if(arr[low][i] == 1)
//                return false;
//        }
        //判断当前位置所放的皇后是否和前面所放的皇后是在同一斜线,如果是,就返回false,表示不可以在这个位置摆放皇后
        for(int i = low - 1; i>=0; i--){
            for(int j = 0; j<n; j++){
                if(arr[i][j] == 1 && Math.abs(low - i) == Math.abs(high - j)){
                    return false;
                }
            }
        }
        return true;//返回true,表示可以在这个位置摆放皇后
    }

有人可能会问不是说任意两个皇后不可以在同一行吗?为什么这里不判断是否在同一行呢?因为第几个皇后就放在第几行,所以任意两个皇后必然不会在同一行,所以这里就不用再去比较当前位置的皇后是否和之前的皇后在同一行了

判断是否在同一条斜线上,必须是通过通过循环嵌套来实现的。
在这里插入图片描述
通过一个二维数组来实现n皇后问题的完整代码:

import java.util.Scanner;

public class EightQueen {
    public static int[][] arr;//新建一个二维数组,表示棋盘
    public static int count = 0;//表示有多少中解法
    public static int n;//表示要放多少个皇后
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        System.out.print("请输入一个数字,表示有多少个皇后:");
        n = sc.nextInt();
        arr = new int[n][n];//表示棋盘,这一步十分重要的,不可以忘记了,否则会发生报错
        getCount(0);//从第一个皇后开始摆放
        System.out.println("可以有 " + count +" 摆法");
    }
    /**
    *这里的num表示的第num + 1个皇后开始摆放,因为他是从下标为0开始摆的
    */
    public static void getCount(int num){
        if(num == n){
            //当num为n时,那么就说明已经摆放了n个皇后了,因为num是从0开始算起的,这时候就可结束递归了
            print();//输出每一种摆法(每一个皇后要摆在它们对应的行的第几列)
            return;
        }
        for(int j = 0; j<n; j++){
            if(judge(num,j)){
                //判断当前的位置是否可以摆放皇后,如果可以就将其赋值为1,然后将摆放下一个皇后
                arr[num][j] = 1;
                getCount(num + 1);//递归完毕之后,将当前位置重置为0,然后后移下一个位置
                arr[num][j] = 0;
            }
        }

    }

    /**
     * 判断是否能够相互攻击----任意两个皇后都不可以在同一列、同一行、同一条斜线上
     * @param low      当前皇后所在位置的行
     * @param high     当前皇后所在位置的列
     * @return          如果返回的是true,表示当前的位置可以摆放皇后,否则返回false,说明当前位置不可以摆放皇后
     */
    public static boolean judge(int low, int high){
        //判断当前位置所放的皇后是否和前面所放的皇后是在同一列,如果是,就返回false,表示不可以在这个位置摆放皇后
        for(int i = low - 1; i>=0; i--){
            if(arr[i][high] == 1)//如果同一列里有皇后,那么返回false
                return false;
        }
//        //判断当前位置所放的皇后是否和前面所放的皇后是在同一行,如果是,就返回false,表示不可以在这个位置摆放皇后
//        for(int i = high - 1; i>=0; i--){
//            if(arr[low][i] == 1)
//                return false;
//        }
        //判断当前位置所放的皇后是否和前面所放的皇后是在同一斜线,如果是,就返回false,表示不可以在这个位置摆放皇后
        for(int i = low - 1; i>=0; i--){
            for(int j = 0; j<n; j++){
                if(arr[i][j] == 1 && Math.abs(low - i) == Math.abs(high - j)){
                //如果同一条斜线上有皇后,那么返回false
                    return false;
                }
            }
        }
        return true;//返回true,表示可以在这个位置摆放皇后
    }
    /**
    *打印出每一种摆法中皇后的具体摆放位置
    */
    public static void print(){
        count++;
        System.out.print("第" + count +" 中解法 ");
        for(int i = 0; i<n; i++){
            for(int j = 0; j<n; j++){
                if(arr[i][j] == 1){
                    System.out.print(j+" ");
                }
            }
        }
        System.out.println();
    }
}

2.一维数组实现n皇后问题

一维数组实现n皇后问题:
思路过程:
1、准备工作:新建一个一维数组,其中它的长度就是n,它的下标 i 表示第 i + 1 个皇后,同时也相当于二维数组中的行,对应的值arr[i]相当于二维数组中皇后所放位置的列,也就是第 arr[i] + 1 列; 定义一个变量count,用来统计一共有多少种方法

2、将第一个皇后放在第一行的第一列,也就是arr[0] = 0。

3、将第二个皇后放在第二行的第一列,然后和之前的皇后进行比较,判断是否能够和之前的皇后相互攻击(任意两个皇后都不能在同一行、同一列、同一斜线,否则就会相互攻击),如果是,那么将当前的位置后移一个位置,否则就说明这个位置可以摆放皇后,那么继续摆放下一个皇后

4、重复步骤3,直到第 n 个皇后摆放完毕,这里说的摆放完毕,就是说最后一个皇后已经遍历完了对应的行的所有情况(相当于二维数组中对应的列都已经遍历完了),此时回溯到上一个皇后,然后再继续上面步骤2、3、4,直到第一个皇后遍历完它的所有情况,也就是相当于第一行的所有列都已经被遍历过了,那么就是实现了n皇后的所有解法。

判断任意两个皇后能否相互攻击思路过程(基于上面对二维数组的理解,会更好):
在这里插入图片描述
代码:

/**
     * 判断当前皇后所放的位置是否会和之前的皇后相互攻击(也就是是否会在同一行、同一列、同一斜线上),如果是,那么就返回false,
     * 表示当前的位置不可以摆放皇后,否则返回true,表示可以摆放皇后
     * @param index  表示第 index + 1个皇后,同时也相当于二维数组中的行
     * @param high   当前皇后所摆放的位置,相当于二维数组的列
     * @return        返回是否可以在当前的位置摆放皇后,如果返回的true,那么说明可以在当前的位置摆放,否则返回false,表示不可以摆放
     */
    public static boolean judge(int index,int high){
        //判断当前的皇后和之前的皇后是否在同一列(因为是一维数组,那么它们对应的值就是所在的列),所以判断是否在同一列,就看当前
        //皇后的值是否和之前的皇后的值相等
        for(int i = index - 1; i>=0; i--){
            if(arr[i] == high)
                return false;
        }
        //判断任意两个皇后是否在同一条斜线上,那么就要看任意两个皇后之间的下标(横坐标)差值的绝对值和他么对应的值(纵坐标)的差的绝对值是否相等,如果相等,就说明了在同一条斜线上,返回false
        for(int i = index - 1; i>=0; i--){
           if(Math.abs(arr[i] - high) == Math.abs(i - index)){
               return false;
           }
        }
        return true;
    }

通过一维数组来实现的n皇后问题完整代码:

import java.util.Scanner;

public class EightQueen2 {
    //一个一维数组表示皇后摆放的位置,其中下标 i表示第 i + 1 个皇后,同时也相当于二维数组中的行,对应的值表示二维数组中的列
    public static int[] arr;
    public static int count = 0;
    public static int n;//表示有n个皇后
    public static void main(String[] args){
        Scanner sc  = new Scanner(System.in);
        System.out.print("请输入有多少个皇后:");
        n = sc.nextInt();
        arr = new int[n];//这一步十分重要,否则会发生报错
        getCount(0);//从第一个皇后开始摆放
        System.out.println("共有 " + count +" 种情况");
    }
    public static void getCount(int num){
        if(num == n){
            //如果num是等于n,那么此时说明了已经摆放了n个皇后,因为num是从0开始算的
            print();
            return;
        }
        for(int j = 0; j<n; j++){
               if(judge(num,j)){
                   //如果可以在这个位置摆放皇后,那么就将其arr[num]赋值为j,表示第 num + 1个皇后摆放在第 num + 1行第 j + 1列
                   arr[num] = j;
                   getCount(num + 1);//摆放好这个皇后以后,那么就将摆放下一个皇后
                   //这一步在二维数组中非常的重要,因为经过上面的递归,会从下一个皇后回溯到当前这个皇后,那么表明了当前皇后放在当前位置的所有情况都考虑,那么就要将当前的皇后摆放到下一个位置,考虑下一个位置的情况,但是在一维数组中这一步是可有可无的,因为这是一维数组,当递归回来以后,当前的皇后就会后移,原来的值就会被覆盖了,所以是可有可无的
                   //arr[num] = 0;
               }
        }
    }

    /**
     * 判断当前皇后所放的位置是否会和之前的皇后相互攻击(也就是是否会在同一行、同一列、同一斜线上),如果是,那么就返回false,
     * 表示当前的位置不可以摆放皇后,否则返回true,表示可以摆放皇后
     * @param index  表示当前是第 index + 1个皇后,同时也表示的是二维数组中对应的行
     * @param high   当前皇后所摆放的位置,表示的是二维数组中对应的列
     * @return        返回是否可以在当前的位置摆放皇后,如果返回的true,那么说明可以在当前的位置摆放,否则返回false,表示不可以摆放
     */
    public static boolean judge(int index,int high){
        //判断当前的皇后和之前的皇后是否在同一列(因为是一维数组,那么它们对应的值就是所在的列),所以判断是否在同一列,就看当前
        //皇后的值是否和之前的皇后的值相等
        for(int i = index - 1; i>=0; i--){
            if(arr[i] == high)
                return false;
        }
        //判断任意两个皇后是否在同一条斜线上,那么就要这两个皇后下标(横坐标)的差的绝对值和他们的值(纵坐标)的差的绝对值是否相等,
        // 如果相等,就说明了在同一条斜线上,返回false
        for(int i = index - 1; i>=0; i--){
            if(Math.abs(arr[i] - high) == Math.abs(i - index)){
                return false;
            }
        }
        return true;
    }

    /**
     * 将每一种摆放方法输出
     */
    public static void print(){
        count++;
        System.out.print("第 " + count +" 种解法:");
        for(int a: arr)
            System.out.print(a+" ");
        System.out.println();
    }
}

上面提到一定要记得写arr = new int[n][n], arr = new int[n];,否则很容易发生报错,这里提醒一下,如果没有写,就会发生这样的错误:
在这里插入图片描述
没有给数组开辟空间,所以在通过二维数组、一维数组来实现时一定要记得这一步arr = new int[n][n]; arr = new int[n]。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值