52. N Queens II
HardDescription
N 皇后問題:在 n x n 的棋盤上放置 n 個皇后,使得任意兩個皇后不能互相攻擊(不同行、不同列、不同對角線)。回傳解的數量。
Example:
Input: n = 4 Output: 2
Intuition#
核心思路:與 51. N-Queens 相同的回溯,但只計數不需要記錄棋盤。
- 每行放一個皇后,用回溯嘗試每列
- 用三個 Set(或 bitmask)追蹤被攻擊的列、主對角線、副對角線
- 因為只需計數,不需要建構棋盤,程式碼更簡潔
Approaches#
1: 回溯 + HashSet#
- 概念: 逐行放置皇后,用 HashSet 記錄被佔用的列和對角線
- 時間複雜度:
O(n!)- 每行的可選列逐步減少 - 空間複雜度:
O(n)
class Solution {
fun totalNQueens(n: Int): Int {
val cols = mutableSetOf<Int>()
val diag1 = mutableSetOf<Int>() // row - col
val diag2 = mutableSetOf<Int>() // row + col
var count = 0
fun backtrack(row: Int) {
if (row == n) {
count++
return
}
for (col in 0 until n) {
if (col in cols || (row - col) in diag1 || (row + col) in diag2) {
continue
}
cols.add(col)
diag1.add(row - col)
diag2.add(row + col)
backtrack(row + 1)
cols.remove(col)
diag1.remove(row - col)
diag2.remove(row + col)
}
}
backtrack(0)
return count
}
}⭐2: 回溯 + Bitmask#
- 概念: 用三個整數的位元表示佔用狀態,位元運算比 HashSet 更快
- 時間複雜度:
O(n!) - 空間複雜度:
O(n)- 遞迴深度
class Solution {
fun totalNQueens(n: Int): Int {
var count = 0
fun backtrack(row: Int, cols: Int, diag1: Int, diag2: Int) {
if (row == n) {
count++
return
}
// 可用位置 = 所有位置 - 被攻擊的位置
var available = ((1 shl n) - 1) and (cols or diag1 or diag2).inv()
while (available != 0) {
// 取最低位的 1(lowbit trick)
val pos = available and (-available)
available = available xor pos
backtrack(
row + 1,
cols or pos,
(diag1 or pos) shl 1,
(diag2 or pos) shr 1
)
}
}
backtrack(0, 0, 0, 0)
return count
}
}🔑 Takeaways#
- Pattern: N 皇后 → 逐行回溯 + 列 / 對角線衝突檢測
- 關鍵技巧: Bitmask 方式用
lowbit = x & (-x)逐一枚舉可用位置;主對角線往左移、副對角線往右移來傳遞攻擊範圍;與 51. N-Queens 的差異僅在於只計數不需記錄解