递归,八皇后问题【Java实现】

1. 问题描述

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

八皇后问题的解法之一:

2. 思路

  1. 将第一个皇后先放第一行第一列;
  2. 将第二个皇后放第二行第一列,然后判断是否会与前面的皇后冲突,如果会,则继续放第二列,第三列……,直到把所有列都放完,找到一个不冲突的位置;
  3. 继续放第三个皇后,还是第一列、第二列、……,直到第八个皇后也能放在一个不冲突的位置,则表示找到一个正确的解;
  4. 当得到一个正确解之后,栈回退到上一个方法,就会开始回溯,即将第一个皇后,放在第一列的所有正确解,全部得到
  5. 然后将第一个皇后放在第一行第二列,继续执行 1, 2, 3 ,4 步骤,直到找到所有解

思路图示:

3. 代码实现

说明:此处使用一个一维数组来表示每个皇后摆放位置,如:arr[8] = {0,4,7,5,2,6,1,3},arr[i] = value 表示第 i+1 个皇后放在第 i+1 行的第 value 列

3.1 判断当前放置的皇后是否会与前面的皇后产生冲突

代码如下:

/**
 * arr[i] == arr[n]:
 *      判断两个皇后是否在同一列
 * Math.abs(n - i) == Math.abs(arr[n] - arr[i]):
 *      判断两个皇后是否在一条斜线上
 *      如果两个位置行列差的绝对值相等(斜率为 1),则表示两个皇后在一条斜线上(冲突)
 *
 * @param n 第 n 个皇后
 * @return 该皇后和前面摆放的皇后是否冲突(冲突返回 false , 不冲突返回 true)
 */
public boolean judge(int n) {
    //每一次冲突,则 计数器加 1
    sum++;
    //循环与前面已经摆放的皇后比较
    for (int i = 0; i < n; i++) {
    	//如果当前摆放皇后与之前摆放的任意皇后在同一列或同一斜线上,则返回false
        if (arr[i] == arr[n] || Math.abs(n - i) == Math.abs(arr[n] - arr[i])) {
            return false;
        }
    }
    return true;
}

3.2 摆放第n个皇后

代码如下:

/**
 * 摆放第 n 个皇后
 * @param n 第 n 个皇后
 */
public void putQueen(int n) {
    //如果当前摆放的是第八个皇后,则表示找到一次正确解
    if (n == max) {
        //打印这一轮结果,且结束这一行的递归,回溯到上一行的下一列
        print();
        return;
    }
    //循环遍历该行的每一列
    for (int i = 0; i < max; i++) {
        //将当前行的当前列赋值给数组元素
        arr[n] = i;
        //如果校验不冲突,则继续摆放下一个皇后
        //如果冲突,则会继续执行循环中该行的下一列
        //如果每一列都冲突,则当前行的 setQueen() 方法出栈,回溯到上一行的 setQueen() 方法
        if (judge(n)) {
            putQueen(n + 1);
        }
    }
}

3.3 打印皇后摆放位置

代码如下:

/**
 * 打印皇后摆放位置
 */
public void print() {
    count++;
    for (int i : arr) {
        System.out.print(i + "\t");
    }
    System.out.println();
}

4. 测试

代码如下:

public class QueensTest {
    /**
     * 皇后个数
     */
    int max = 8;

    /**
     * 用于保存皇后放置位置
     * 如:{0,4,7,5,2,6,1,3}
     *      表示第 1 行第 1 列放第一个皇后,第 2 行第 4 列放第二个皇后,依此类推
     */
    int[] arr = new int[max];

    /**
     * 正确解法种数计数器
     */
    public static int count = 0;

    /**
     * 在求解的过程中皇后冲突次数计数器
     */
    public static int sum = 0;

    public static void main(String[] args) {
        Queens queens = new Queens();
        //从第一行第一列开始递归
        queens.putQueen(0);
        System.out.printf("一共有 %d 种解法\n", count);
        System.out.printf("在求解的过程中,一共产生了 %d 次冲突", sum);
    }
}

测试结果:

0	4	7	5	2	6	1	3
0	5	7	2	6	3	1	4
0	6	3	5	7	1	4	2
0	6	4	7	1	3	5	2
1	3	5	7	2	0	6	4
1	4	6	0	2	7	5	3
1	4	6	3	0	7	5	2
1	5	0	6	3	7	2	4
1	5	7	2	0	3	6	4
1	6	2	5	7	4	0	3
1	6	4	7	0	3	5	2
1	7	5	0	2	4	6	3
2	0	6	4	7	1	3	5
2	4	1	7	0	6	3	5
2	4	1	7	5	3	6	0
2	4	6	0	3	1	7	5
2	4	7	3	0	6	1	5
2	5	1	4	7	0	6	3
2	5	1	6	0	3	7	4
2	5	1	6	4	0	7	3
2	5	3	0	7	4	6	1
2	5	3	1	7	4	6	0
2	5	7	0	3	6	4	1
2	5	7	0	4	6	1	3
2	5	7	1	3	0	6	4
2	6	1	7	4	0	3	5
2	6	1	7	5	3	0	4
2	7	3	6	0	5	1	4
3	0	4	7	1	6	2	5
3	0	4	7	5	2	6	1
3	1	4	7	5	0	2	6
3	1	6	2	5	7	0	4
3	1	6	2	5	7	4	0
3	1	6	4	0	7	5	2
3	1	7	4	6	0	2	5
3	1	7	5	0	2	4	6
3	5	0	4	1	7	2	6
3	5	7	1	6	0	2	4
3	5	7	2	0	6	4	1
3	6	0	7	4	1	5	2
3	6	2	7	1	4	0	5
3	6	4	1	5	0	2	7
3	6	4	2	0	5	7	1
3	7	0	2	5	1	6	4
3	7	0	4	6	1	5	2
3	7	4	2	0	6	1	5
4	0	3	5	7	1	6	2
4	0	7	3	1	6	2	5
4	0	7	5	2	6	1	3
4	1	3	5	7	2	0	6
4	1	3	6	2	7	5	0
4	1	5	0	6	3	7	2
4	1	7	0	3	6	2	5
4	2	0	5	7	1	3	6
4	2	0	6	1	7	5	3
4	2	7	3	6	0	5	1
4	6	0	2	7	5	3	1
4	6	0	3	1	7	5	2
4	6	1	3	7	0	2	5
4	6	1	5	2	0	3	7
4	6	1	5	2	0	7	3
4	6	3	0	2	7	5	1
4	7	3	0	2	5	1	6
4	7	3	0	6	1	5	2
5	0	4	1	7	2	6	3
5	1	6	0	2	4	7	3
5	1	6	0	3	7	4	2
5	2	0	6	4	7	1	3
5	2	0	7	3	1	6	4
5	2	0	7	4	1	3	6
5	2	4	6	0	3	1	7
5	2	4	7	0	3	1	6
5	2	6	1	3	7	0	4
5	2	6	1	7	4	0	3
5	2	6	3	0	7	1	4
5	3	0	4	7	1	6	2
5	3	1	7	4	6	0	2
5	3	6	0	2	4	1	7
5	3	6	0	7	1	4	2
5	7	1	3	0	6	4	2
6	0	2	7	5	3	1	4
6	1	3	0	7	4	2	5
6	1	5	2	0	3	7	4
6	2	0	5	7	4	1	3
6	2	7	1	4	0	5	3
6	3	1	4	7	0	2	5
6	3	1	7	5	0	2	4
6	4	2	0	5	7	1	3
7	1	3	0	6	4	2	5
7	1	4	2	0	6	3	5
7	2	0	5	1	4	6	3
7	3	0	2	5	1	6	4
一共有 92 种解法
一共产生了 15720 次冲突
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值