N皇后问题

题目

  • n皇后问题研究的是如何将n个皇后放置在n*n的棋盘上。并且使皇后彼此之间不能相互攻击。皇后之间不能在同一行,同一列,正斜,反斜上同时存在

在这里插入图片描述

  • 上图为 8 皇后问题的一种解法。
  • 给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
  • 每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

示例:
输入: 4
输出: [
[".Q…", // 解法 1
“…Q”,
“Q…”,
“…Q.”],
["…Q.", // 解法 2
“Q…”,
“…Q”,
“.Q…”]
]
解释: 4 皇后问题存在两个不同的解法。

解析:回溯搜索

思路分析

  • 我们这里以4皇后为例,他的“搜索”过程如下,大家完全可以在纸上模拟过程:
    在这里插入图片描述
  • 以下假定是给棋盘的每一行从左到右标记为1,2,3,4
    在这里插入图片描述
  • 因为是一行一行进行摆放的,因此这些“皇后”一定不在同一行中,无需额外进行设置状态
  • 为了保证其不再同一个列中,即不能出现类似[2,2,2,2]这种只有另两个相同的情况,我们用col的集合来存放记录摆放到棋盘中的哪一列,若再次摆放的时候,当前列已经在该set中,即列重复。
  • 副对角线不能重复,其规律就是:行 + 列 = 常数
    在这里插入图片描述
  • 主对角线不能重复,其规律就是:行 - 列 = 常数
    在这里插入图片描述
  • 我们按行存放,若放置成功,则跳到下一行,若不成功,继续下一列,若当前行的列数遍历完毕,还没有找到合适的,进行回溯,将上一个皇后重新放置。重复过程。参考上面的动图

代码如下

  • 详细可以参考注释
package com.xiyou.solutio;

import java.util.*;

/**
 * @author 92823
 * n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
 *
 *
 *
 * 上图为 8 皇后问题的一种解法。
 *
 * 给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
 *
 * 每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
 *
 * 示例:
 *
 * 输入: 4
 * 输出: [
 *  [".Q..",  // 解法 1
 *   "...Q",
 *   "Q...",
 *   "..Q."],
 *
 *  ["..Q.",  // 解法 2
 *   "Q...",
 *   "...Q",
 *   ".Q.."]
 * ]
 * 解释: 4 皇后问题存在两个不同的解法
 *
 * 横排 竖排 斜着都不能有皇后
 */
public class HuangHou {
    public List<List<String>> solveNQueens(int n) {
        List<List<String>> res = new ArrayList<>();
        if (n == 0) {
            // 如果当前传进来的是0行
            return res;
        }
        // 当前数组用来存放每一列的值,不去管行
        int[] nums = new int[n];
        // 赋值每一列的元素
        for (int i = 0; i < n; i++) {
            nums[i] = i;
        }
        // 存放当前哪一列中有值
        Set<Integer> col = new HashSet<>();
        // 主对角线存值
        Set<Integer> master = new HashSet<>();
        // 从对角线存值
        Set<Integer> slave = new HashSet<>();
        // 存放保存的值到stack中
        Stack<Integer> stack = new Stack<>();
        backtrack(nums, 0, n, col, master, slave, stack, res);
        return res;
    }


    /**
     * 棋盘:
     * 1 2 3 4
     * 1 2 3 4
     * 1 2 3 4
     * 1 2 3 4
     * 递归调用,往棋盘中放皇后
     * @param nums 里面存放的是棋子, 值是当前的列
     * @param row  当前是第几行
     * @param n 棋盘的长度
     * @param col 用来判断当前列中是否有重复的
     * @param master 用来判断主对角线是否有重复的
     * @param slave 用来判断次对角线是否有重复的
     * @param stack 当前已经存放进棋盘的棋子
     * @param res 最终的返回结果
     */
    private void backtrack(int[] nums,
                           int row,
                           int n,
                           Set<Integer> col,
                           Set<Integer> master,
                           Set<Integer> slave,
                           Stack<Integer> stack,
                           List<List<String>> res) {
        // 递归退出的条件,已经遍历完毕了,前几行都放已经处理完了(要么遍历到最后都没有满足条件的)
        if (row == n) {
            // 直接将当前值进行转换
            List<String> board = convert2board(stack, n);
            res.add(board);
            return;
        }
        // 针对每一列 尝试是否可以放置
        for (int i = 0; i < n; i++) {
            // 判断当前列上是否有这个值
            // 行就不用判断了,一行只有一个反正
            // 判断主对角线是否有值 主对角线上的值 row + i = 常量
            // 判断从对角线是否有值 从对角线上的值 row - i = 常量
            if (!col.contains(i) && !master.contains(row + i) && !slave.contains(row - i)) {
                // 如果当前列和主对角线和从对角线都没有存放这个元素
                // 存放当前值
                stack.add(nums[i]);
                // 存放列,表示当前列有值
                col.add(i);
                // 存放主对角线,表示当前主对角线有值
                master.add(row + i);
                // 存放主对角线,表示当前从对角线有值
                slave.add(row - i);
                // 递归调用
                backtrack(nums, row + 1, n, col, master, slave, stack, res);
                // 进行回溯
                // 从对角线删除上一个元素
                slave.remove(row - i);
                // 主对角线删除上一个元素
                master.remove(row + i);
                // 删除列,表示当前列没有值
                col.remove(i);
                // 栈中的值去除,表示当前栈去除当前值
                stack.pop();
            }
        }
    }


    /**
     * 进行返回值的转换,将stack中的值转换成list
     * @param stack
     * @param n
     * @return
     */
    private List<String> convert2board(Stack<Integer> stack, int n) {
        List<String> board = new ArrayList<>();
        for (Integer num : stack) {
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < n; i++) {
                // 先把每一个的元素赋值为'.'
                stringBuilder.append(".");
            }
            // 替换,将指定位置上的.替换成Q
            stringBuilder.replace(num, num+1, "Q");
            // 一行的值处理完毕,添加到列表中
            board.add(stringBuilder.toString());
        }
        return board;
    }



    public static void main(String[] args) {
        HuangHou huangHou = new HuangHou();
        huangHou.solveNQueens(4);
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值