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

Posted android超级兵

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法: 八皇后问题(详细流程)相关的知识,希望对你有一定的参考价值。

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

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

采用到的知识点:

  • 递归
  • 一维数组

什么是8皇后问题?

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

例如这样

定义规则

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

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;

当前效果:

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

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

辅助图:

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

当前点:(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]) 每一列相减的绝对值

目前的效果图:

递归调用代码分析

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

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);
            
        
    		

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

慢慢分析,由浅入深!

先来分析循环:

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

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

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

效果图就是这样的:

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

循环第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);
        
    
		

再来看一眼效果图:

最终最终就会通过:

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目录

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

以上是关于数据结构与算法: 八皇后问题(详细流程)的主要内容,如果未能解决你的问题,请参考以下文章

八皇后问题的遗传算法解法,c语言编写

8皇后问题SQL求解(回溯算法)

回溯法解决八皇后问题

回溯法解决八皇后问题

Java与算法之 - 八皇后问题

八皇后问题的两个高效的算法(回溯与递归)