Hard
题目描述
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
解题思路
N皇后问题是经典的回溯算法应用。核心思想是逐行放置皇后,对于每一行尝试在每一列放置皇后,同时检查是否与之前放置的皇后冲突。
关键观察:
- 每行只能放置一个皇后
- 每列只能有一个皇后
- 每条对角线只能有一个皇后
解法思路: 使用回溯算法,从第0行开始,逐行放置皇后。对于每一行,尝试在每一列放置皇后,检查是否与已放置的皇后冲突:
- 列冲突:同一列已有皇后
- 主对角线冲突:行号-列号相同的位置已有皇后
- 副对角线冲突:行号+列号相同的位置已有皇后
优化技巧: 为了快速检查冲突,使用三个集合分别记录已占用的列、主对角线和副对角线。主对角线用(row-col)标识,副对角线用(row+col)标识。
当成功放置n个皇后时,将当前解加入结果集。回溯时移除当前皇后并尝试下一个位置。
时间复杂度较高但这是该问题的最优解法,因为需要遍历所有可能的解空间。
代码实现
class Solution {
public:
vector<vector<string>> solveNQueens(int n) {
vector<vector<string>> result;
vector<string> board(n, string(n, '.'));
unordered_set<int> cols, diag1, diag2;
backtrack(0, n, board, cols, diag1, diag2, result);
return result;
}
private:
void backtrack(int row, int n, vector<string>& board,
unordered_set<int>& cols, unordered_set<int>& diag1,
unordered_set<int>& diag2, vector<vector<string>>& result) {
if (row == n) {
result.push_back(board);
return;
}
for (int col = 0; col < n; col++) {
if (cols.count(col) || diag1.count(row - col) || diag2.count(row + col)) {
continue;
}
board[row][col] = 'Q';
cols.insert(col);
diag1.insert(row - col);
diag2.insert(row + col);
backtrack(row + 1, n, board, cols, diag1, diag2, result);
board[row][col] = '.';
cols.erase(col);
diag1.erase(row - col);
diag2.erase(row + col);
}
}
};
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
result = []
board = ['.' * n for _ in range(n)]
cols = set()
diag1 = set() # row - col
diag2 = set() # row + col
def backtrack(row):
if row == n:
result.append(board[:])
return
for col in range(n):
if col in cols or (row - col) in diag1 or (row + col) in diag2:
continue
board[row] = board[row][:col] + 'Q' + board[row][col + 1:]
cols.add(col)
diag1.add(row - col)
diag2.add(row + col)
backtrack(row + 1)
board[row] = board[row][:col] + '.' + board[row][col + 1:]
cols.remove(col)
diag1.remove(row - col)
diag2.remove(row + col)
backtrack(0)
return result
public class Solution {
public IList<IList<string>> SolveNQueens(int n) {
var result = new List<IList<string>>();
var board = new string[n];
for (int i = 0; i < n; i++) {
board[i] = new string('.', n);
}
var cols = new HashSet<int>();
var diag1 = new HashSet<int>();
var diag2 = new HashSet<int>();
Backtrack(0, n, board, cols, diag1, diag2, result);
return result;
}
private void Backtrack(int row, int n, string[] board, HashSet<int> cols,
HashSet<int> diag1, HashSet<int> diag2, IList<IList<string>> result) {
if (row == n) {
result.Add(new List<string>(board));
return;
}
for (int col = 0; col < n; col++) {
if (cols.Contains(col) || diag1.Contains(row - col) || diag2.Contains(row + col)) {
continue;
}
var chars = board[row].ToCharArray();
chars[col] = 'Q';
board[row] = new string(chars);
cols.Add(col);
diag1.Add(row - col);
diag2.Add(row + col);
Backtrack(row + 1, n, board, cols, diag1, diag2, result);
chars[col] = '.';
board[row] = new string(chars);
cols.Remove(col);
diag1.Remove(row - col);
diag2.Remove(row + col);
}
}
}
var solveNQueens = function(n) {
const result = [];
const board = Array(n).fill().map(() => Array(n).fill('.'));
const isValid = (row, col) => {
for (let i = 0; i < row; i++) {
if (board[i][col] === 'Q') return false;
}
for (let i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if (board[i][j] === 'Q') return false;
}
for (let i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
if (board[i][j] === 'Q') return false;
}
return true;
};
const backtrack = (row) => {
if (row === n) {
result.push(board.map(row => row.join('')));
return;
}
for (let col = 0; col < n; col++) {
if (isValid(row, col)) {
board[row][col] = 'Q';
backtrack(row + 1);
board[row][col] = '.';
}
}
};
backtrack(0);
return result;
};
复杂度分析
| 复杂度类型 | 分析 |
|---|---|
| 时间复杂度 | O(N!) - 第一行有N种选择,第二行最多N-1种,以此类推,总的可能性为N!级别 |
| 空间复杂度 | O(N) - 递归栈深度为N,额外使用三个集合存储冲突信息,每个集合最多N个元素 |
相关题目
. N-Queens II (Hard)
. Grid Illumination (Hard)