回溯算法解决八皇后问题(JAVA实现)

送给程序猿们一句话
《拥有水滴石穿的坚持;懂得聚沙成塔的积累;磨练坚韧不拔的意志;学习脚踏实地的奋斗;提升立世做人的技巧;突破自我设限的障碍。》

背景

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。计算机发明后,有多种计算机语言可以解决此问题。
在这里插入图片描述

问题解决

思路

这道题的关键在于每一个对角线和行或者列上面只能有一个皇后,否则就不满足八皇后的约束条件。所以我们每一行只能有一个皇后切必须有一个皇后。因此,我们完全可以使用一维数组来搞定这个事情,我们可以定义一个int[8]的数组,然后我们用数组的下脚标当做第几行,用值当做第几列,就可以完美的承载八皇后的解法数据。这样,我们就搞定了数据结构。有了数据结构之后,我们就要去寻找解决这个问题的算法,很明显这种探求问题全部解的是需要我们的回溯算法的。

什么是回溯算法

回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。

算法详解

上面说了,我们用小脚标来定义行,数组的值来定义列。 我们先把一个皇后放到第一行第一列,然后第二个皇后放到第二行的第一列,发现已经不合乎条件了,我们就放到第二行第二列,还是不合乎条件,我们就放到第二行第三列,好像可以了,我们接着放第三行的,什么时候才算成功呢?当我们把第八行放完了,第八个还是满足条件,那不就是八皇后搞定了吗?然后我们回溯第八行再放其他的位置,完事之后第七行再放其他的位置…回溯到第一行之后,我们接着试把皇后放到第一行第二列…
通过这种做法,我们就会把这个事情所有的情况都试了一遍(当然,我们发现第二排第一列不行了,我们会立马试第二列,因为再往下也不行了,第二行就挂了)

我们怎么判断是否满足规则

当我们放第四行的时候,说明前三行已经有皇后了,那么我们只需要需要挨个判断一下前三个和这第四个是不是在一列上,是不是在一个对角线上(肯定不在同一行,因为我们是一行一行放的,下脚标加一就代表着第几行)。
那么我们怎么判断是不是在对角线呢,这里需要点数学。不理解我们记住就好。对角线的行之差的绝对值等于列之差的绝对值,至于怎么判断是不是在一列上面就更简单了,我们的值就代表着列,只需要第四个值和前三个值都不相等就不在同一列即可。就这样,放第一行的时候不用判断,第二行的时候熊第一列开始判断和第一行的那位皇后冲不冲,不冲突从第三行第一列放第三个皇后…第八个皇后满足条件之后就满足了。

代码

/**
 * @Description: 八皇后问题Java实现
 * @Author: YangHang
 * @Date: 2019/7/12 12:57
 */
public class EightQueueDemo {

    public static void main(String[] args) {
        EightQueueDemo queue = new EightQueueDemo();
        queue.check(0);
        System.out.println("一共有<" + queue.count + ">种解法, 并且要循环<" + queue.loopCount + ">次");
    }

    /**
     * 八皇后数组,下脚标代表行, 数值代表列
     */
    int arr[] = new int[8];

    /**
     * 一共有多少种解法
     */
    int count = 0;

    /**
     * 求出所有结果需要遍历多少次
     */
    int loopCount = 0;

    public void check(int n) {
        if (n == 8) {
            print();
            count++;
            return;
        }

        for (int i = 0; i < arr.length; i++) {
            arr[n] = i;
            if (ifSatisfied(n)) {
                check(n + 1);
            }
        }
    }

    /**
     * 判断当前的方法是否合乎八皇后的逻辑
     */
    public boolean ifSatisfied(int n) {
        loopCount++;
        for (int i = 0; i < n; i++) {
            // 判断不在一列和不在一个对角线上(对角上的书行相减的绝对值等于列相减的绝对值)
            if (arr[i] == arr[n] || Math.abs(arr[i] - arr[n]) == Math.abs(i - n)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 打印八皇后问题的解法
     */
    public void print() {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
}

运行结果

在这里插入图片描述
一共92种解法,我们可以去网上找个八皇后的小游戏玩一玩,看看对不对。
但是记住我们数组是从0开始的哈。

总结

学习算法,思路最重要,一定要想明白再敲代码。相信这个案例是一个学习回溯算法的很好的经典案例。(有什么不明白的可以给我留言)

  • 18
    点赞
  • 74
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员大航子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值