【Leetcode hot 100】51.N皇后
问题链接
51.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 <= n <= 9
问题解答
解题思路
- 核心约束:皇后不能同行、同列、同斜线(45°和135°),因此可通过「按行放置」规避同行问题(每行仅放1个皇后)。
- 回溯逻辑:
- 终止条件:当遍历到第
n
行(行号从0开始)时,说明所有皇后已合法放置,将当前棋盘加入结果集。 - 递归尝试:对当前行的每一列,判断该位置是否合法(无同列、同斜线皇后),若合法则放置皇后,递归处理下一行,最后回溯(撤销当前皇后,尝试下一列)。
- 终止条件:当遍历到第
- 合法性校验:
- 检查当前列的上方是否有皇后(同列约束)。
- 检查当前位置左上斜线(行-1、列-1方向)是否有皇后(45°约束)。
- 检查当前位置右上斜线(行-1、列+1方向)是否有皇后(135°约束)。
完整 Java 代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;class Solution {// 存储所有合法的 N 皇后方案private List<List<String>> result = new ArrayList<>();public List<List<String>> solveNQueens(int n) {// 初始化棋盘:n×n 大小,初始值为 '.'(空位)char[][] chessboard = new char[n][n];for (char[] row : chessboard) {Arrays.fill(row, '.');}// 从第 0 行开始回溯backtrack(n, 0, chessboard);return result;}/*** 回溯函数:尝试在第 row 行放置皇后* @param n 棋盘大小* @param row 当前处理的行号(从0开始)* @param chessboard 当前棋盘状态*/private void backtrack(int n, int row, char[][] chessboard) {// 终止条件:所有行都已放置皇后(row == n)if (row == n) {// 将 char[][] 棋盘转换为 List<String> 格式,加入结果集result.add(convertChessboard(chessboard));return;}// 遍历当前行的每一列,尝试放置皇后for (int col = 0; col < n; col++) {// 校验当前位置 (row, col) 是否合法if (isValid(row, col, chessboard, n)) {// 放置皇后chessboard[row][col] = 'Q';// 递归处理下一行backtrack(n, row + 1, chessboard);// 回溯:撤销当前位置的皇后,尝试下一列chessboard[row][col] = '.';}}}/*** 校验 (row, col) 位置是否合法(无同列、同斜线皇后)* @param row 当前行号* @param col 当前列号* @param chessboard 棋盘状态* @param n 棋盘大小* @return 合法返回 true,否则 false*/private boolean isValid(int row, int col, char[][] chessboard, int n) {// 1. 检查当前列的上方是否有皇后(同列约束)for (int i = 0; i < row; i++) {if (chessboard[i][col] == 'Q') {return false;}}// 2. 检查左上斜线(行-1,列-1)是否有皇后(45°约束)for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {if (chessboard[i][j] == 'Q') {return false;}}// 3. 检查右上斜线(行-1,列+1)是否有皇后(135°约束)for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {if (chessboard[i][j] == 'Q') {return false;}}return true;}/*** 将 char[][] 格式的棋盘转换为 List<String> 格式* @param chessboard 原始棋盘* @return 转换后的字符串列表*/private List<String> convertChessboard(char[][] chessboard) {List<String> board = new ArrayList<>();for (char[] row : chessboard) {board.add(new String(row));}return board;}
}
代码解释
- 初始化棋盘:通过
char[][]
存储棋盘(String 不可变,char 数组更易修改),初始所有位置为.
。 - 回溯函数
backtrack
:- 按行遍历,每行尝试所有列;
- 合法位置放置皇后后,递归处理下一行;
- 递归返回后回溯(恢复为
.
),确保后续尝试不受影响。
- 合法性校验
isValid
:- 仅需检查「上方」的列和斜线(下方未处理,无皇后),减少冗余计算;
- 斜线遍历通过「行-1、列±1」实现,边界条件(
i >=0
、j >=0
、j <n
)避免数组越界。
- 棋盘转换
convertChessboard
:将char[][]
每行转换为 String,符合题目返回格式List<List<String>>
。
复杂度分析
- 时间复杂度:
O(n! * n)
。n! 是所有可能的列选择(每行1列,无同列),每个方案需O(n²)
校验合法性(实际优化后为O(n)
,仅检查上方),整体近似O(n! * n)
。 - 空间复杂度:
O(n²)
。递归栈深度为n
(行号从0到n-1),棋盘存储为n²
,结果集存储所有方案(最坏情况O(n! * n)
,不计入算法本身空间)。