N皇后问题


一、N皇后问题描述

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位
示例1:
在这里插入图片描述
输入:n = 4
输出:[[“.Q…”,“…Q”,“Q…”,“…Q.”],[“…Q.”,“Q…”,“…Q”,“.Q…”]]
解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:

输入:n = 1
输出:[[“Q”]]

二、解题思路

1.回溯法

用 DFS 的回溯法求解从树的根节点 [][] 开始按照深度逐个遍历
回溯法算实际上就是一个决策树(因为在每个节点上都要做决策)的遍历过程:

路径:也就是已经做出的选择。
选择列表:也就是当前可以做的选择。
结束条件:也就是到达决策树底层(选择列表为空的时候),无法再做出选择的条件。
与全排列问题差不多,决策树的每一层表示棋盘上的每一行;每个节点可以做出的选择是,在该行任意一列放置一个皇后。
首先,创建棋盘,其中 ’ . ’ 表示空,’ Q ’ 表示皇后,初始化空棋盘;

然后,调用 backtrack()backtrack() 方法:
“路径”:boardboard 中小于 rowrow 的那些行都已经成功放置了皇后;
“选择列表”:第 rowrow 行的所有列都是放置皇后的选择;
“结束条件”:rowrow 超过 boardboard 的最后一行,说明棋盘放满了。

关键代码

class Solution {
    List<List<String>> res=new ArrayList<>();
    public List<List<String>> solveNQueens(int n) {
        char[][] board=new char[n][n];// 定义棋盘大小,并初始化棋盘
        for (char[] c : board) Arrays.fill(c, '.');
        backtrack(n, board, 0);
        return res;
    }
    // 是否可以在 board[row][col] 位置放置皇后
    public boolean isValid(char[][] board, int row, int col, int n) {
        // 检查列中是否有皇后互相冲突
        for (int i=0;i<row;i++) {
            if (board[i][col] == 'Q') return false;
        }

        // 检查右上方是否有皇后互相冲突
        for (int i=row-1, j=col+1;i>=0 && j<n;i--, j++) {
    
            if (board[i][j] == 'Q') return false;
        }

        // 检查左上方是否有皇后互相冲突
        for (int i=row-1, j=col-1;i>=0 && j>=0;i--, j--) {
            if (board[i][j] == 'Q') return false;
        }
        return true;
    }
    // 将 char[][] 中的每一行转换成 List 集合
    public List<String> Array2List(char[][] board) {
        List<String> list = new ArrayList<>();
        for (char[] c : board) {
            list.add(String.copyValueOf(c));
        }
        return list;
    }
    public void backtrack(int n, char[][] board, int row) {
        if (row == n) {
            res.add(Array2List(board));
            return ;
        }
        for (int col=0;col<n;col++) {
            if (!isValid(board, row, col, n)) continue; // 剪枝
            board[row][col] = 'Q'; // 做选择
            backtrack(n, board, row + 1);
            board[row][col] = '.'; // 撤销选择
        }
    }
}

剪枝

要判断是否可以在 board[row][col]board[row][col] 位置放置皇后:因为皇后是从上往下一行一行放置的,所以只需要检查正上方、左上方、右上方三个方向,而不用检查当前行和下面的三个方向。

若在需要检查的三个方向上存在 board[i][j] = ‘Q’board[i][j]= ‘Q’
,则说明在 board[row][col]board[row][col] 位置上不能够放置皇后,此时直接跳过该位置,继续遍历下一个位置即可。

// 是否可以在 board[row][col] 位置放置皇后
public boolean isValid(char[][] board, int row, int col, int n) {

    // 检查列中是否有皇后互相冲突
    for (int i=0;i<row;i++) {

        if (board[i][col] == 'Q') return false;
    }

    // 检查右上方是否有皇后互相冲突
    for (int i=row-1, j=col+1;i>=0 && j<n;i--, j++) {
        
        if (board[i][j] == 'Q') return false;
    }

    // 检查左上方是否有皇后互相冲突
    for (int i=row-1, j=col-1;i>=0 && j>=0;i--, j--) {

        if (board[i][j] == 'Q') return false;
    }
    return true;
}
if (!isValid(board, row, col, n)) continue; // 剪枝

当在 “做选择” 时,就是从 “选择列表” 中拿出一个 “选择”,并将它放入到 “路径” 中。
若当前位置 board[row][col]board[row][col] 可以放置皇后,则令 board[row][col] = 'Q’board[row][col]=

Q

,并且在调用 backtrack()backtrack() 方法时,传入的参数中的行数应该 + 1+1,即 row + 1row+1 以进行下一行的决策。

结束条件:若当前传入的参数 row == nrow==n,说明已经遍历完棋盘 boardboard 的最后一行了,此时棋盘已经放满了,需要先将当前棋盘 boardboard 中的每一行都都转换成 StringString 类型的 listlist 集合,然后将该集合添加到 resres 中并回溯到上一层,将上一层 borad[row][col]borad[row][col] 位置上的皇后拿掉,即 board[row][col] = '.'board[row][col]= ’,‘。
(回溯到上一层后,继续遍历选择列表,选择下一个分支)(“撤销选择”:就是从 “路径” 中拿出一个选择,将它恢复到 “选择列表” 中)。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值