八皇后算法原理理解 递归回溯算法 JAVA代码实现

灵感

在b站看到了这位师兄的讲解,基本框架说得很清楚了。在此主要说明一些细节以及算法回溯的地方。
点击此处跳转 -> b站八皇后算法讲解

八皇后问题

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

思路

思考方向

因为要用递归回溯解决问题。那么有几个思考方向:

  1. 递归终点是什么?
  2. 递归计算公式是什么?
  3. 在什么时候产生回溯?

难点

  1. 递归终点并不像斐波那契或者杨辉三角那样明确:第一个或是最后一个。
  2. 数据类型不是简单的基本数据类型。计算公式也不明确。

明确点

  1. 能够知道的是,我们的行为是:放置皇后
  2. 棋盘相当于一个地图,可以用二维数组,也可以用一维数组
    • 一维数组的下标代表行数,一维数组的元素值代表列数。
    • 即:j 棋子坐标:(j , arr[j])
    • 比如 arr[3] = 2 ,那么(3,2)即第四行第三列。放了一个棋子。
  3. 有八个皇后,可以一个个放置。
  4. 八皇后规则:
    简化图arr[i] == arr[j]
    Math.abs(j - i) == Math.abs(arr[j] - arr[i])

  1. 每个皇后有八个位置可以放(从第一列到第八列)-> 8轮外循环
    for (arr[j] = 0; arr[j] < 8; arr[j]++)
  2. 先试探着放下去,然后和之前已经放置好的棋子做判断:
    • 放置的是 j 皇后(第 j+1 个皇后),则前面有 j 个皇后已经放好了。需要和这 j 个皇后进行比较 -> j 轮内循环
      for (i = 0; i < j; i++)
    • 如果违反八皇后规则,则跳出内循环(不再比较),继续外循环,即 j 皇后右移一位。
    • 如果进行 j 轮内循环后都没有违反八皇后规则,则arr[j]确定下来,寻找下一个皇后 -> 重复345步 (递归)
      if(i == j) put(arr,j+1)
      PS:这是个经典的判断中途是否有跳出循环的做法,就是增长的变量(i)是否等于边界值(j)

回溯

理论理解

我们知道,递归调用时,会中断当前执行的程序,进入调用的方法。这个方法调用完成时,会返回调用这个方法的地方。

  • 那么,就只需要看两点:

    • 在哪里调用了方法(递归)
      • 我们在 j 轮内循环完成后,调用了方法。if(i == j) put(arr,j+1)
    • 调用方法的地方,后面还有没有语句需要执行。如果有,就会产生回溯
      • put(arr,j+1)往后看,发现我们后面还有一部分外循环没有执行
      • 程序总是在前面 j 个皇后满足规则后就中断,调用方法找后面的皇后。
  • 具体什么时候产生回溯:在调用的方法执行完毕时,会产生回溯

    • 可以看到,外循环执行完了,一个方法也就结束了 。
    • 外循环执行完毕 => j 皇后从第一列到第八列都没有找到能不违反规则的位置。此时 j 皇后的列数自增为8

图文理解

模块分析

输出模块(print)

int count = 1;
public void print(int[] arr,int count){
    int i;
    System.out.println("第" + count + "种摆法:");
    for (i = 0; i < 8; i++) {
        System.out.print(arr[i] + " ");
    }
    System.out.println("");
}

主要是count,需要定义在方法外面。
否则每次递归调用的时候,会导致count总是被初始化,出现始终为1等异常情况。

放置模块(put)

public void put(int[] arr,int j) { //放置第j+1个皇后
    if (j == 8) {  //没有第九个棋子,所以j增加到8时则说明八皇后已经放置完毕,可以输出
        print(arr,count++);
    } else {
        int i;
        for (arr[j] = 0; arr[j] < 8; arr[j]++) {
            for (i = 0; i < j; i++) { //固定后面的放置(j)的棋子,判断前面已经放好的棋子(i)是否与它(j)冲突
                if (arr[i] == arr[j] || Math.abs(j - i) == Math.abs(arr[j] - arr[i])) {
                    break; //产生冲突,不能放在这里
                }
            }//没有和前面任何一个棋子冲突,则放置该棋子,判断下一个棋子
            if(i == j) put(arr,j+1);
        }//遍历所有列都不能满足条件:自动退出->回溯到调用方法的地方。
        //也就是上一行的put(arr,j+1),会继续执行外层for循环。即:将前一个j的列数增加
    }
}
  • 输出模块没有在main调用,是因为j == 8的时候,一种摆法就已经生成了(理应输出)。而put没有返回值,所以main是接收不到的。
  • 所以main直接调用put就可以得到结果。
    main调用put,put自行递归,put再调用print

主类主方法(main)

public class EightQueen {
    public static void main(String[] args){
        int[] arr = new int[8];
        int j = 0;
        Tool tool = new Tool();
        tool.put(arr,j); 
    }
}

代码实现

public class EightQueen {
    public static void main(String[] args){
        int[] arr = new int[8];
        int j = 0;
        Tool tool = new Tool();
        tool.put(arr,j);
    }
}

class Tool {
    int count = 1;
    public void print(int[] arr,int count){
        int i;
        System.out.println("第" + count + "种摆法:");
        for (i = 0; i < 8; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println("");
    }
    public void put(int[] arr,int j) { //放置第j+1个皇后
        if (j == 8) {  //没有第九个棋子,所以j增加到8时则说明八皇后已经放置完毕,可以输出
            print(arr,count++);
        } else {
            int i;
            for (arr[j] = 0; arr[j] < 8; arr[j]++) {  //j皇后在它的行进行遍历判断 (也是回溯后执行的语句)
                for (i = 0; i < j; i++) { //固定后面的放置(j)的棋子,判断前面已经放好的棋子(i)是否与它(j)冲突
                    if (arr[i] == arr[j] || Math.abs(j - i) == Math.abs(arr[j] - arr[i])) {
                        break; //产生冲突,不能放在这里
                    }
                }//没有和前面任何一个棋子冲突,则放置该棋子,判断下一个棋子
                if(i == j) put(arr,j+1);
            }//遍历所有列都不能满足条件:自动退出->回溯到调用方法的地方。
            //也就是上一行的put(arr,j+1),会继续执行外层for循环。即:将前一个j的列数增加
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值