LeetCode 第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- 皇后彼此不能相互攻击,也就是说:任何两个皇后都不能处于同一行、同一列或同一斜线上。
解题思路
回溯算法 + 剪枝
这是一道经典的回溯算法题目。关键是要理解皇后的攻击范围,并使用合适的数据结构来判断位置是否可用。
关键点:
- 使用回溯法逐行放置皇后
- 使用三个集合记录已经被攻击的列、正对角线和反对角线
- 利用数学关系快速判断对角线
- 需要将结果转换为题目要求的棋盘表示
具体步骤:
- 初始化记录攻击位置的集合
- 从第一行开始,尝试在每一列放置皇后
- 检查当前位置是否可用
- 放置皇后并更新攻击范围
- 递归处理下一行
- 回溯时恢复状态
图解思路
算法步骤分析表
| 步骤 | 当前行 | 已放置皇后 | 操作 | 说明 |
|---|---|---|---|---|
| 初始 | 0 | [] | 开始 | 空棋盘 |
| 第1步 | 0 | [1] | 放置第1个 | 第一行第二列 |
| 第2步 | 1 | [1,3] | 放置第2个 | 第二行第四列 |
| 第3步 | 2 | [1,3,0] | 放置第3个 | 第三行第一列 |
| 完成 | 3 | [1,3,0,2] | 得到解 | 完整方案 |
状态/情况分析表
| 情况 | 输入 | 输出 | 说明 |
|---|---|---|---|
| n=1 | 1 | [["Q"]] | 最简单情况 |
| n=4 | 4 | 2个解决方案 | 标准情况 |
| n=8 | 8 | 92个解决方案 | 复杂情况 |
代码实现
C# 实现
public class Solution {
private IList<IList<string>> result = new List<IList<string>>();
private HashSet<int> cols = new HashSet<int>();
private HashSet<int> diag1 = new HashSet<int>();
private HashSet<int> diag2 = new HashSet<int>();
public IList<IList<string>> SolveNQueens(int n) {
int[] queens = new int[n];
Backtrack(queens, 0, n);
return result;
}
private void Backtrack(int[] queens, int row, int n) {
if (row == n) {
result.Add(GenerateBoard(queens, n));
return;
}
for (int col = 0; col < n; col++) {
// 检查当前位置是否可用
if (cols.Contains(col) ||
diag1.Contains(row + col) ||
diag2.Contains(row - col)) {
continue;
}
// 放置皇后
queens[row] = col;
cols.Add(col);
diag1.Add(row + col);
diag2.Add(row - col);
// 递归下一行
Backtrack(queens, row + 1, n);
// 回溯,移除皇后
cols.Remove(col);
diag1.Remove(row + col);
diag2.Remove(row - col);
}
}
private IList<string> GenerateBoard(int[] queens, int n) {
var board = new List<string>();
for (int i = 0; i < n; i++) {
char[] row = new char[n];
Array.Fill(row, '.');
row[queens[i]] = 'Q';
board.Add(new string(row));
}
return board;
}
}
执行结果
- 执行用时:168 ms
- 内存消耗:42.8 MB
代码亮点
- 🎯 使用HashSet快速判断位置可用性
- 💡 巧妙利用数学关系处理对角线
- 🔍 优化回溯过程,及时剪枝
- 🎨 代码结构清晰,模块化设计
常见错误分析
- 🚫 对角线判断条件错误
- 🚫 回溯时状态恢复不完整
- 🚫 棋盘生成格式错误
- 🚫 集合使用不当导致状态混乱
解法对比
| 解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| 暴力枚举 | O(n!×n) | O(n) | 思路简单 | 效率极低 |
| 回溯+剪枝 | O(n!) | O(n) | 较好效率 | 实现复杂 |
| 位运算优化 | O(n!) | O(n) | 性能最佳 | 难以理解 |