LeetCode 36 要求判斷一個 9×9 數獨盤面是否合法(非求解)。合法定義:每行、每列、每個 3×3 子網格內,數字 1~9 至多出現一次,空格用 '.' 表示。本文展示從 unordered_set 到位掩碼的三種解法,並分析時間空間權衡。
為每行、每列、每宮各維護一個 unordered_set<char>,遍歷盤面,若數字已在對應集合中則衝突。
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
unordered_set<char> rows[9], cols[9], boxes[3][3];
for (int i = 0; i < 9; ++i) {
for (int j = 0; j < 9; ++j) {
char c = board[i][j];
if (c == '.') continue;
if (rows[i].count(c) || cols[j].count(c) || boxes[i/3][j/3].count(c))
return false;
rows[i].insert(c);
cols[j].insert(c);
boxes[i/3][j/3].insert(c);
}
}
return true;
}
};
利用 int 的低 9 位作為位掩碼:第 k 位為 1 表示數字 k+1 已出現。int rows[9][9] = {0} 中 rows[i][d] 記錄第 i 行數字 d+1 是否出現。
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
int rows[9][9] = {0}, cols[9][9] = {0}, boxes[3][3][9] = {0};
for (int i = 0; i < 9; ++i) {
for (int j = 0; j < 9; ++j) {
char c = board[i][j];
if (c == '.') continue;
int d = c - '1'; // 0~8 對應 1~9
if (rows[i][d]++ || cols[j][d]++ || boxes[i/3][j/3][d]++)
return false;
}
}
return true;
}
};
每行、每列、每宮各用一個 int(9 位)即可,總共 27 個 int。遍歷時動態計算宮索引。
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
int rowMask[9] = {0}, colMask[9] = {0}, boxMask[9] = {0};
for (int i = 0; i < 9; ++i) {
for (int j = 0; j < 9; ++j) {
char c = board[i][j];
if (c == '.') continue;
int bit = 1 << (c - '1');
int b = (i / 3) * 3 + j / 3; // 宮索引 0~8
if (rowMask[i] & bit) return false;
if (colMask[j] & bit) return false;
if (boxMask[b] & bit) return false;
rowMask[i] |= bit;
colMask[j] |= bit;
boxMask[b] |= bit;
}
}
return true;
}
};
| 指標 | unordered_set | 定長數組計數 | 位掩碼整數 |
|---|---|---|---|
| 時間 | ~24 ms | ~12 ms | ~8-10 ms |
| 空間 | ~KB 級(哈希桶) | ~300 bytes | ~108 bytes |
| 代碼複雜度 | 低 | 低 | 中 |
| 可讀性 | 最高 | 高 | 中(需位運算基礎) |
| 擴展性 | 易擴展至 N×N | 固定 9×9 | 固定 9×9(位數受限) |
box = (i/3)*3 + j/3,將 3×3 宮映射為 0~8。1 << (c - '1'),數字 1→bit 0, 9→bit 8。if (rows[i][d]++) 利用舊值為 0/1 直接判斷衝突,極其簡潔。return false,無需掃完整盤面。vector<unordered_set> 或 vector<vector<bool>>,位掩碼受限於整型位寬(64 位支援至 8×8)。