本文目录
- 1 中文题目
- 2 求解方法:位运算+回溯法
- 2.1 方法思路
- 2.2 Python代码
- 2.3 复杂度分析
- 3 题目总结
1 中文题目
n 皇后问题
研究的是如何将 n
个皇后放置在 n × n
的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n
,返回 n
皇后问题 不同的解决方案的数量。
示例:
输入:n = 4
输出:2
解释:如上图所示,4 皇后问题存在两个不同的解法。
输入:n = 1
输出:1
提示:
- 1 ≤ n ≤ 9 1 \leq n \leq 9 1≤n≤9
2 求解方法:位运算+回溯法
2.1 方法思路
方法核心
使用位运算优化的回溯算法,通过三个整数的二进制位来分别记录已占用的列和两个方向的对角线,利用位运算快速计算可用位置并更新状态,避免了使用集合带来的开销,同时通过位运算的特性快速获取和更新可用位置。
实现步骤
(1)状态表示:
- 使用整数的二进制位表示棋盘状态,每个二进制位1表示该位置被占用
- columns记录列的占用情况,diagonals1记录主对角线占用情况,diagonals2记录副对角线占用情况
(2)可用位置计算:
- 使用位运算计算当前行可用位置
- 通过OR运算合并所有限制
- 通过NOT运算取反获得可用位置
- 通过AND运算限制在棋盘范围内
(3)位置选择和更新:
- 使用位运算获取最右边的可用位置
- 更新三个状态值
- 递归处理下一行
- 统计所有可能的解数量
(4)回溯过程:
- 对每一行遍历所有可用位置
- 递归处理下一行
- 累加所有可能的解数量
- 当处理完最后一行时返回1
方法示例
以 n = 4 为例:
初始状态:
row = 0
columns = 0000
diagonals1 = 0000
diagonals2 = 0000
第一步(第一行):
available_positions = 1111
选择position = 0001(最右位置)
递归到下一行:
columns = 0001
diagonals1 = 0010
diagonals2 = 0000
第二步(第二行):
available_positions = 1100
选择position = 0100
递归到下一行:
columns = 0101
diagonals1 = 1010
diagonals2 = 0010
第三步(第三行):
available_positions = 1000
选择position = 1000
递归到下一行:
columns = 1101
diagonals1 = 0100
diagonals2 = 0101
第四步(第四行):
available_positions = 0010
选择position = 0010
找到一个解:
.Q..
...Q
Q...
..Q.
继续回溯找其他解...
2.2 Python代码
class Solution:
def totalNQueens(self, n: int) -> int:
# 使用整数来记录已占用的列和对角线
# columns记录已占用的列
# diagonals1记录已占用的主对角线(从左上到右下)
# diagonals2记录已占用的副对角线(从右上到左下)
def backtrack(row: int, columns: int, diagonals1: int, diagonals2: int) -> int:
# 当遍历完所有行,找到一个有效解
if row == n:
return 1
# 记录当前行所有可能解的数量
count = 0
# 当前行可用位置的二进制表示
# (~(columns | diagonals1 | diagonals2)) & ((1 << n) - 1)
# 计算当前行所有可放置皇后的位置
available_positions = ((1 << n) - 1) & \
(~(columns | diagonals1 | diagonals2))
# 遍历所有可用位置
while available_positions:
# 获取最低位的1(即最右边可用的位置)
position = available_positions & (-available_positions)
# 将当前位置从可用位置中移除
available_positions = available_positions & (available_positions - 1)
# 递归到下一行,更新三个限制条件
# columns | position:在columns中标记当前列已使用
# (diagonals1 | position) << 1:主对角线限制在下一行的位置
# (diagonals2 | position) >> 1:副对角线限制在下一行的位置
count += backtrack(row + 1,
columns | position,
(diagonals1 | position) << 1,
(diagonals2 | position) >> 1)
return count
return backtrack(0, 0, 0, 0)
2.3 复杂度分析
- 时间复杂度:O(N!)
- 第一行有N种选择
- 第二行最多有N-1种选择
- 第三行最多有N-2种选择
- 依此类推
- 总的时间复杂度约为O(N!)
- 位运算优化使得实际运行时间远小于理论值
- 空间复杂度:O(N)
- 递归调用栈深度为N:O(N)
- 使用三个整数存储状态:O(1)
3 题目总结
题目难度:困难
数据结构:数组
应用算法:位运算、回溯法