数据结构与算法

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

2022-07-05  本文已影响0人  紫荆秋雪_文

铭记:

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

源码下载

一、场景

二、思路

三、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();
    }

}

上一篇下一篇

猜你喜欢

热点阅读