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"]]
算法思路
该代码通过 回溯法 解决 N 皇后问题,尝试在棋盘上逐行放置皇后,并通过剪枝条件确保皇后之间不冲突。核心思路如下:
算法步骤
-
递归终止条件:
- 当
res
的长度等于n
时,表示所有皇后已合法放置,将当前棋盘状态保存到结果列表res_list
。
- 当
-
遍历当前行的所有列:
- 对每一列
i
,将其加入res
(表示当前行皇后放在第i
列)。 - 调用
check
函数检查当前放置是否合法。 - 若合法,则递归处理下一行。
- 无论是否合法,回溯时弹出
res
的最后一个元素(撤销当前行的选择)。
- 对每一列
-
冲突检查(
check
函数):- 检查新加入的皇后是否与之前行的皇后存在列冲突或斜线冲突。
- 列冲突:新皇后与任意旧皇后在同一列。
- 斜线冲突:新皇后与任意旧皇后在同一正斜线(行差 = 列差)或反斜线(行差 = -列差)。
-
结果格式转换(
built
函数):- 将列索引列表转换为题目要求的字符串格式(如
[1,3]
→".Q..", "..Q."
)。
- 将列索引列表转换为题目要求的字符串格式(如
关键点
- 剪枝策略:仅在当前行皇后放置合法时,才继续递归下一行。
- 冲突检查逻辑:通过遍历已放置的皇后,检查列和斜线冲突(时间复杂度为 O ( n ) O(n) O(n))。
- 回溯状态管理:通过
res.append(i)
和res.pop()
维护路径状态。 - 结果转换:将列索引转换为棋盘字符串,保证输出符合要求。
复杂度分析
-
时间复杂度:O(n! × n)
- 最坏情况下(无剪枝),遍历所有可能的排列组合(n!),每次递归需 O ( n ) O(n) O(n) 时间检查冲突。
- 实际因剪枝优化,复杂度低于 O ( n ! × n ) O(n! × n) O(n!×n),但仍较高。
-
空间复杂度:O(n²)
- 递归栈深度为 O ( n ) O(n) O(n)。
- 结果列表存储所有解的棋盘字符串,每个解占用 O ( n 2 ) O(n²) O(n2) 空间(n 个字符串,每个长 n)。
算法代码
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
def check(res):
if not res: return True
target = res[-1]
for i in range(len(res)-1):
if target - (len(res)-(i+1)) == res[i] \
or target + (len(res)-(i+1)) == res[i] \
or target == res[i]:
return False
return True
def backtrack(index=0,res=[]):
# 时空气泡
if not check(res):
return res
if len(res) == n:
res_list.append(res[:])
return res
# 探索子时空
for i in range(0,n):
res.append(i)
res = backtrack(index+1,res)
res.pop()
# 回溯时空气泡
return res
def built(res_list):
result = []
for res in res_list:
_str_list = []
for i in res:
_str = ['.'] * n
_str[i] = 'Q'
_str_list.append("".join(_str))
result.append(_str_list)
return result
res_list = []
backtrack()
return built(res_list)
代码问题与优化
现存问题
-
递归参数传递错误:
backtrack
函数中res = backtrack(index+1, res)
会覆盖当前路径状态,导致后续res.pop()
操作错误。- 修复:直接通过
append
和pop
维护res
,无需重新赋值。
-
冲突检查效率低:
check
函数每次遍历所有已放置皇后,时间复杂度为 O(n)。- 优化:使用哈希集合记录被占用的列、正斜线(行-列)、反斜线(行+列),将冲突检查降至 O(1)。
-
冗余参数
index
:index
未被使用,可移除以简化逻辑。
优化后代码示例
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
def backtrack(row, cols, diag1, diag2, path):
if row == n:
res.append(["." * i + "Q" + "." * (n - i - 1) for i in path])
return
for col in range(n):
d1, d2 = row - col, row + col
if col not in cols and d1 not in diag1 and d2 not in diag2:
backtrack(row + 1, cols | {col}, diag1 | {d1}, diag2 | {d2}, path + [col])
res = []
backtrack(0, set(), set(), set(), [])
return res