美文网首页数据结构与算法
08-递归-回溯-8皇后问题

08-递归-回溯-8皇后问题

作者: 紫荆秋雪_文 | 来源:发表于2022-07-05 15:24 被阅读0次

铭记:

\color{#DC143C}{算法 + 数据结构 = 程序}

源码下载

一、场景

  • 八皇后问题:在一个8X8的棋盘上,放置8个棋子
  • 要求:任意两个棋子不能处于同一行、同一列、同一斜线。问有多少摆法?

二、思路

  • 1、在二维数组中,从第一行的第一列开始一个一个放置,直到第八列
  • 2、从第二行的第一列开始一个一个放置,直到第八列
  • 3、不断的执行1、2步骤,判断是否可以放置皇后
  • 4、这里会涉及到回溯的问题:
  • 4.1、回溯的第一种情况:当某一行(第5行)都不适合放置皇后,那么就需要回溯到第4行,并且说明第4行当前位置放置的皇后是错误的,需要撤销并且把皇后放置到下一列再试尝试1,2步骤。
  • 4.2、回溯的第二种情况:完成一次放置(如:7 2 0 5 1 4 6 3)需要逐层回退(在1-5位置不变的情况下)移动第6行的皇后到下一列,然后再继续1,2步骤
  • 回溯:表示我们之前的走法是行不通或者是行得通但是要尝试另一条路,例如步骤4中的回溯情况。例如回溯情况4.2,由于已经把第6、7、8行都已经走过,所以需要把回溯行到最后一行重新恢复原样(防止判断错误)

三、Queen8

package com.raven.alg.s5recursion;

/**
 * 8 皇后问题
 * 思路:
 * 首先放置第一个皇后
 * 固定在第一行,从第一列开始
 * (0, 1),(0, 2),(0, 3),(0, 4),(0, 5),(0, 6),(0, 7)
 */
public class Queen8 {

    // 已使用,需要跳过
    private final static Integer SUCCESS = 1;
    private final static Integer FAIL = 2;
    private final static Integer HS = 3;

    private static Boolean isHS = false;
    private static Boolean isFinish = false;
    private static Integer count = 0;

    public static void run(Integer size) {
        if (size < 1) {
            throw new RuntimeException("棋盘不能小于0");
        }
        // 1、创建棋盘
        int[][] mg = initMigong(size);
        // 棋盘
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                System.out.print(mg[i][j] + " " + " ");
            }
            System.out.println();
        }
        // 第一个皇后的位置
        go(mg, 0);
        System.out.println("Count = " + count);
    }

    private static void go(int[][] mg, int startX) {
        // 已放满
        if (startX == mg.length) {
            count++;
            System.out.println("已完成第 " + count + " 次");
            print2(mg);

            print(mg);
            isFinish = true;
            System.out.println();
            return;
        }

        // 从 0 ~ 7
        for (int i = 0; i < mg.length; i++) {
            // 处理回溯
            if (isHS || isFinish) {
                isHS = false;
                recall(mg, startX);
            }
            // 完成时进行下一遍是需要把当前回溯
            if (isFinish) {
                isFinish = false;
                recall(mg, startX);
            }
            mg[startX][i] = 1;
            if (SUCCESS == check(mg, startX, i)) {
                go(mg, startX + 1);
            } else {
                mg[startX][i] = 2;
            }
        }
    }

    /**
     * 校验当前位置的皇后是否与已经存在的皇后冲突
     * <p>
     * 冲突:不能在同一列、同一行、一条斜线上
     *
     * @param mg
     * @param startX
     * @param startY
     * @return
     */
    private static Integer check(int[][] mg, int startX, int startY) {

        for (int i = 0; i < startX; i++) {
            for (int j = 0; j < mg.length; j++) {
                int hh = mg[i][j];
                boolean isX = i == startX;
                boolean isY = j == startY;
                Boolean isXX = Math.abs(i - startX) == Math.abs(j - startY);
                if (hh == SUCCESS && (isX || isY || isXX)) {
                    // 需要回溯
                    if (mg.length - 1 == startY) {
                        isHS = true;
                    }
                    return FAIL;
                }
            }
        }
        return SUCCESS;
    }


    /**
     * 回溯
     *
     * @param mg
     * @param startX
     * @return
     */
    private static void recall(int[][] mg, int startX) {
        for (int i = startX; i < mg.length; i++) {
            for (int j = 0; j < mg.length; j++) {
                if (1 == mg[i][j]) {
                    mg[i][j] = HS;
                }
            }
        }
    }


    /**
     * 打印
     *
     * @param mg
     */
    private static void print2(int[][] mg) {
        for (int i = 0; i < mg.length; i++) {
            for (int j = 0; j < mg.length; j++) {
                if (1 == mg[i][j]) {
                    System.out.print(j + "   ");
                }
            }
        }
        System.out.println();
    }

    /**
     * 打印
     *
     * @param mg
     */
    private static void print(int[][] mg) {
        System.out.println("已完成第 " + count + " 次8皇后图:");
        for (int i = 0; i < mg.length; i++) {
            for (int j = 0; j < mg.length; j++) {
                System.out.print(mg[i][j] + "   ");
            }
            System.out.println();
        }
    }

    /**
     * 初始化棋盘
     *
     * @param size
     * @return
     */
    private static int[][] initMigong(Integer size) {
        if (size < SUCCESS) {
            throw new RuntimeException("迷宫不能小于0");
        }
        // 创建迷宫
        int[][] mg = new int[size][size];
        return mg;
    }

}

四、这种方法更加取巧

这个方法比较取巧,但是思路一样,定义一个一维数组arr = {0 , 4, 7, 5, 2, 6, 1, 3},arr的下标代表第几行的皇后,arr的值表示皇后所在的列。

package com.raven.alg.s5recursion;

public class Queue82 {

    //数组大小
    int max = 8;
    // arr = {0 , 4, 7, 5, 2, 6, 1, 3}
    int[] array = new int[max];
    static int count = 0;
    static int judgeCount = 0;
    public static void main(String[] args) {
        Queue82 queue8 = new Queue82();
        queue8.check(0);
        System.out.printf("共有%d种ⷨ", count);
        System.out.printf("共回溯%d次", judgeCount); // 1.5w
        System.out.println();
    }



    private void check(int n) {
        if(n == max) {  //n = 8 代表防止成功
            print();
            return;
        }

        //表示每个皇后都是从0列开始
        for(int i = 0; i < max; i++) {
            //第n+1个皇后防止在第i+1列表
            array[n] = i;
            //判断是否可以放置皇后在n+1位置
            if(judge(n)) {
                // 防止下一行
                check(n+1);
            }
        }
    }

    /**
     *  判断是否可以放置皇后
     * @param n
     * @return
     */
    private boolean judge(int n) {
        judgeCount++;
        for(int i = 0; i < n; i++) {
            //1. array[i] == array[n]  表示在同一列
            //2. Math.abs(n-i) == Math.abs(array[n] - array[i]) 表示在一个斜线上
            // n = 1  ���õ� 2�� 1 n = 1 array[1] = 1
            // Math.abs(1-0) == 1  Math.abs(array[n] - array[i]) = Math.abs(1-0) = 1
            if(array[i] == array[n] || Math.abs(n-i) == Math.abs(array[n] - array[i]) ) {
                return false;
            }
        }
        return true;
    }

    //输出
    private void print() {
        count++;
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + " ");
        }
        System.out.println();
    }

}

相关文章

  • 08-递归-回溯-8皇后问题

    铭记: 源码下载[https://github.com/zjqx1991/alg] 一、场景 八皇后问题:在一个8...

  • LeetCode-N Queens

    N皇后问题。经典回溯问题: 以下是递归及非递归的方法:

  • 八皇后问题(N皇后问题)

    八皇后问题是一个经典的递归回溯问题。 描述 八皇后问题是在一个 8*8 的棋盘上放置皇后,要求其放置后满足同一行,...

  • Leetcode 51. N-Queens

    回溯法,非递归求 N 皇后问题所有解,Python 3 实现: 源代码已上传 Github,持续更新。 源代码已上...

  • Leetcode 52. N-Queens II

    回溯法,非递归求 N 皇后问题解个数,Python 3 实现: 源代码已上传 Github,持续更新。 源代码已上...

  • 算法-递归回溯N皇后问题

    51. N 皇后[https://leetcode-cn.com/problems/n-queens/] 皇后的摆...

  • 递归回溯算法:八皇后问题

    1、环境配置: 系统:win10 编程语言:C++ 编译器:DevC++ 2、算法思想: 2.1、回溯的基本思想:...

  • N皇后

    回溯法核心代码: n皇后问题回溯法

  • 2n皇后问题

    题目描述: 解决方法:递归+回溯先铺上一层皇后,再铺一层

  • 重写算法:八皇后问题

    八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。 问题的内容是在国际象棋棋盘上(8*8),放置八个皇后并...

网友评论

    本文标题:08-递归-回溯-8皇后问题

    本文链接:https://www.haomeiwen.com/subject/bwesbrtx.html