数据结构与算法: 八皇后问题(详细流程)

数据结构与算法: 八皇后回溯递归问题(详细流程!!)

Tips: 采用java语言, 关注博主,底部附有完整代码

采用到的知识点:

  • 递归
  • 一维数组

什么是8皇后问题?

在一个8 x 8 的二维数组上, 每一个位置 , 斜面 不允许有另一个元素存在

例如这样

image-20220511151529978

定义规则

这里只需要定义一个存放每一列的一维数组即可,这样就可以保证每一行都不会有相同的元素

for(i = 0; i < 8 ; i++){
   // 循环每一行
}

例如上面效果图对应的就是 0 , 4 , 7 ,5 , 4, 6, 1, 3

对应成二维数组就是 [0,0] , [1,4] , [2,3] , [3,5] , [4,2] , [5,6] , [6,1] , [7,3]

先来看完整代码,在逐步分析!

/*
 * @author: android 超级兵
 * @create: 2022-05-11 13:34
 * TODO
 **/
public class Client {
    // 女皇个数
    int mQueenMax = 8;

    // 存放每一列的坐标
    int[] mQueens = new int[mQueenMax];

    // 记录一共打印的次数
    private int count = 0;

    public static void main(String[] args) {
        Client client = new Client();

        // 从第0行开始递归
        client.check(0);

        System.out.printf("执行完毕 一共有%d种解法", client.count);
    }

    // 用来递归赋值
    public void check(int k) {
        if (k == mQueenMax) {
            // 打印当前数据
            show();
            return;
        }
        // 循环每一个皇后 每一行
        for (int i = 0; i < mQueenMax; i++) {
            mQueens[k] = i;

            // 判断是否满足列不相同,斜线上不相同的条件,如果满足就开始递归,进行下一行的赋值
            if (isContains(k)) {
              // 递归调用 只要满足条件就会开始递归下一行 
                check(k + 1);
            }
        }
    }

    /*
     * TODO 判断之前所有行,判断是否包含某个皇后
     * @param k: 每一行
     */
    public boolean isContains(int k) {
        System.out.println("isContains = " + k);
        // 循环每一行
        for (int i = 0; i < k; i++) {
            // mQueens[i] == mQueens[n] 判断列是否相等

            // Math.abs(n - i) == Math.abs(mQueens[n] - mQueens[i])
            //  判断斜线上是否相等
            //  如果两个点在同一条斜线上,那么他们俩 行相减 和 列相减 的值肯定相同
            // 如果列上有相同的点 或者 斜线上有相同的点
            if (mQueens[i] == mQueens[k] || Math.abs(k - i) == Math.abs(mQueens[k] - mQueens[i])) {
                return false;
            }
        }
        System.out.println();
        return true;
    }

    // 打印当前数据 
    public void show() {
        count++;
        for (int item : mQueens) {
            System.out.printf("%d\t", item);
        }
        System.out.println();
    }
}

分析

如何做到每一列上的点各不相同

通俗的说就是一维数组中元素各不相同

/**
* @param k: 行
*/
public boolean isContains(int k) {
    // 循环每一行,为了判断每一列是否有相同的元素
    for (int i = 0; i < k; i++) {
        // mQueens[i] 每一行的元素
        // mQueens[k] 当前的值
        // mQueens[i] == mQueens[k] 判断列是否相等
        // 如果相等,那么则表示每一行有相同的元素,返回false
        if (mQueens[i] == mQueens[k]) {
            return false;
        }
    }
    // 循环完所有的,没有列相同的元素,返回true
    return true;
}

当前效果:

gif_1

如何做到斜线上没有相同的元素

假设当前点是(4,5) , 需要判断 斜线上的点,那么则需要判断这些点

辅助图:

image-20220512093918581

判断两个点是否在同一条斜线上,那么他们俩 行相减列相减绝对值一定相同

当前点:(4 ,5)

随机选取几个点: (6, 3) (0,1) (3,6) (2,7) (6,7)

  • 4 - 6 = -2 行相减
    5 - 3 = 2 列相减

  • 4 - 0 = 4 行相减
    5 - 1 = 4 列相减

  • 4-3 = 1

    5-6 = -1

  • 4 -2 = 2

    5-7 = -2

  • 4-6 = -2

    5-7 = -2

那么这段代码就应该这么写:

/**
* @param k: 行
*/
public boolean isContains(int k) {
    // 循环每一行
    for (int i = 0; i < k; i++) {
        // mQueens[i] 数组中的每一个值
        // mQueens[k] 当前的值
        // mQueens[i] == mQueens[k] 判断列是否相等
        // 如果相等,那么则表示每一列有相同的元素,返回false
        if (mQueens[i] == mQueens[k]|| Math.abs(k - i) == Math.abs(mQueens[k] - mQueens[i])) {
            return false;
        }
    }
    // 循环完所有的,没有列相同的元素,返回true
    return true;
}
  • Math.abs(k - i) 每一行相减的绝对值
  • Math.abs(mQueens[k] - mQueens[i]) 每一列相减的绝对值

目前的效果图:

gif_2

递归调用代码分析

接下来在分析一下这段代码

int mQueenMax = 8;
 // 用来递归赋值
    public void check(int k) {
        if (k == mQueenMax) {
            // 打印当前数据
            show();
            return;
        }
        // 循环每一个皇后 每一行
        for (int i = 0; i < mQueenMax; i++) {
            mQueens[k] = i;

            // 判断是否满足列不相同,斜线上不相同的条件,如果满足就开始递归,进行下一行的赋值
            if (isContains(k)) {
              // 递归调用 只要满足条件就会开始递归下一行 
                check(k + 1);
            }
        }
    }		

这里一定要把眼睛擦亮,是循环每一行里面嵌套递归!

慢慢分析,由浅入深!

先来分析循环:

既然是循环每一行,那么效果应该是这样的:

八皇后gif_3

但是,在代码中, 首先通过 isContains()方法来判断是否有列相同,或者斜线相同的元素

在0的位置一定满足条件,那么就会往下执行递归

效果图就是这样的:

gif_2

然后一直走一直走,最终大致流程是这样的:

八皇后gif_4

循环第8次的时候,就会发现每一个元素都不满足条件,都不满足条件的话本次递归就结束了,然后继续接着执行第7次的循环

再来看看这段代码

int mQueenMax = 8;
 // 默认第一次的时候传入0
public void check(int k) {
    // 如果k == 8 就结束递归
    if (k == mQueenMax) {
      show();
      return;
    }
  	// 循环每一个皇后 每一行
    for (int i = 0; i < 8; i++) {
      mQueens[k] = i;

      	// 判断是否满足列不相同,斜线上不相同的条件,如果满足就开始递归,进行下一行的赋值
        if (isContains(k)) {
          // 递归调用 只要满足条件就会开始递归下一行 
          check(k + 1);
        }
    }
}		

再来看一眼效果图:

八皇后gif_5

最终最终就会通过:

if (k == mQueenMax) {
  show();
  return;
}

public void show() {
  count++;
  for (int item : mQueens) {
    System.out.printf("%d\t", item);
  }
  System.out.println();
}

这段代码来打印第一次的结果0, 4, 7, 5, 2, 6, 1, 3

看到这里你以为就结束了嘛? 虽然有return

但是别忘记了,这是递归,第7次的for循环还在继续呢. 所以还需要一直走

这样的解法一共有92种

虽然只有不到60行代码,但这一段代码绝对值得深思,功底太深厚了! 可惜原创不是我 🥹

好了 本篇就结束了! 886

完整代码,还有更多算法代码,请看gitee目录

原创不易,您的关注与点赞就是对我最大的支持!

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

s10g

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

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

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

打赏作者

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

抵扣说明:

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

余额充值