題目描述:給定一個 9×9 的數獨棋盤(部分格子已填入數字 1–9,空格用 '.' 表示),請原地填入所有空格,使其成為一個有效的數獨解答。保證輸入恰好有唯一解。有效數獨規則:每行、每列、每個 3×3 子宮格各含 1–9 各一次。
解題思路:採用**回溯法(Backtracking)**逐格試填。
為了加速合法性檢查,使用三組位元掩碼(bitmask)預先記錄狀態:
rowMask[r]:第 r 行已使用的數字集合(bit 1 表示數字 1 已用)colMask[c]:第 c 列已使用的數字集合boxMask[b]:第 b 個 3×3 宮格(b = (r/3)*3 + c/3)已使用的數字集合回溯流程:
'.')。true。時間複雜度:O(9^M) — M 為空格數量(最多 81 個)。每個空格最多嘗試 9 種數字,最壞情況下需要回溯所有組合。但由於數獨的約束性很強(行、列、宮格三重過濾),實際上剪枝效果極好,執行非常快。
空間複雜度:O(1) — 位元掩碼陣列大小固定(3 組各 9 個整數),棋盤原地修改;遞迴深度最多 81 層,也視為常數。
1. Initialize rowMask[9], colMask[9], boxMask[9] all to 0
2. For each pre-filled cell (r, c) with digit d:
- Set bit d in rowMask[r], colMask[c], boxMask[(r/3)*3 + c/3]
3. Function solve(board) -> bool:
a. Scan board for the next empty cell ('.') at (r, c)
b. If no empty cell found, return true (puzzle solved)
c. Compute box index b = (r/3)*3 + (c/3)
d. For each digit d from 1 to 9:
- If bit d is set in rowMask[r] OR colMask[c] OR boxMask[b]: skip
- Place d: board[r][c] = d, set bit d in masks
- If solve(board) returns true: return true
- Backtrack: board[r][c] = '.', clear bit d from masks
e. Return false (no valid digit for this cell)
4. Call solve(board)方法一:朴素回溯(Naive Backtracking with Set Lookup)
不使用位元掩碼,改用三組 unordered_set 分別記錄每行、每列、每個宮格已使用的數字。合法性檢查為 O(1) 平均,但常數較大。程式碼更易理解,適合初學者。時間複雜度 O(9^M),空間複雜度 O(81) = O(1)。
方法二:MRV 啟發式回溯(Minimum Remaining Values Heuristic) 每次不是線性找第一個空格,而是選取「合法候選數字最少」的空格優先填入(即約束最強的格子)。這大幅減少回溯次數,在實際測試中速度比朴素版快 5–10 倍。結合 Forward Checking(每次填數後立刻傳播約束)可進一步剪枝,是數獨求解器競賽中的標準策略。時間複雜度理論上仍為 O(9^M),但實際執行極快。