题目
Write a program to solve a Sudoku puzzle by filling the empty cells.
A sudoku solution must satisfy all of the following rules:
Each of the digits 1-9
must occur exactly once in each row.
Each of the digits 1-9
must occur exactly once in each column.
Each of the the digits 1-9
must occur exactly once in each of the 9 3x3 sub-boxes of the grid.
Empty cells are indicated by the character ‘.’.
A sudoku puzzle…
…and its solution numbers marked in red.
Note:
- The given board contain only digits 1-9 and the character ‘
.
’. - You may assume that the given Sudoku puzzle will have a single unique solution.
- The given board size is always 9x9.
思路
- 先遍历,记录需要填数字的坐标和各行、各列、各小格子已经被填好的数字,考虑到代码清晰度,坐标用结构Slot{x, y}来表示,所有的空格用数组slots记录。考虑空间利用效率和随后的计算效率,每个数字用一个bit位表示,用三个数组就可以表示各行、各列、各小格子已经填好的数字,这里可以理解为数独的初始状态;
- 遍历slots,当前slot可填的数字(通过位操作即可得到可填入的数字集合,用bit来表示可填入的数字,一个整形即可以表示该集合)。将当前该slot所在行、列、小格子的初始状态,可填入数字记录,并压栈,然后取其中一个数,填入board,并重新计算该slot所在的行、列、小格子的状态,继续遍历;
- 遍历时,如果计算的可填入数字为0,则回溯。回溯前需还原该格子涉及的状态;每取一次可填入数字时,将对应的bit位置0,标记该值已经尝试过,下次不再尝试。
Code in C++
class Solution {
struct State {
int rowFlag;
int colFlag;
int boxFlag;
int avail;
State(int r, int c, int b, int a):rowFlag(r), colFlag(c), boxFlag(b), avail(a) {}
};
struct Slot {
int x;
int y;
Slot(int a, int b): x(a), y(b) {}
};
public:
void solveSudoku(vector<vector<char>>& board) {
int cursor = 0;
stack<State> flags;
vector<int> rowFlags(9, 0);
vector<int> colFlags(9, 0);
vector<int> boxFlags(9, 0);
vector<Slot> slots;
for (int i = 0; i < 9; ++i) {
for (int j = 0; j < 9; ++j) {
if (board[i][j] == '.')
slots.push_back(Slot(i, j));
else {
int digit = 1 << (board[i][j] - '1');
rowFlags[i] |= digit;
colFlags[j] |= digit;
boxFlags[(i/3) + 3*(j/3)] |= digit;
}
}
}
int i = 0, slotsSize = slots.size();
int x = slots[i].x, y = slots[i].y;
int avail = 0x1FF ^ (rowFlags[x] | colFlags[y] | boxFlags[(x/3) + 3*(y/3)]);
flags.push(State(rowFlags[x], colFlags[y], boxFlags[(x/3) + 3*(y/3)], avail));
while (!flags.empty()) {
int bIdx = slots[i].x/3 + 3*(slots[i].y/3);
// 开始尝试该位置的可用值;
State &s = flags.top();
if(s.avail != 0) {
int bit = GetAndSetAvail(board, slots[i], s.avail);
rowFlags[slots[i].x] = s.rowFlag | bit;
colFlags[slots[i].y] = s.colFlag | bit;
boxFlags[bIdx] = s.boxFlag | bit;
++i;
if (i == slots.size())
break;
bIdx = slots[i].x/3 + 3*(slots[i].y/3);
avail = 0x1FF ^ (rowFlags[slots[i].x] | colFlags[slots[i].y] | boxFlags[bIdx]);
flags.push(State(rowFlags[slots[i].x], colFlags[slots[i].y], boxFlags[bIdx], avail));
}
else { // 回溯
rowFlags[slots[i].x] = s.rowFlag;
colFlags[slots[i].y] = s.colFlag;
boxFlags[bIdx] = s.boxFlag;
flags.pop();
--i;
}
}
return;
}
int GetAndSetAvail(vector<vector<char>> &board, Slot &s, int &avail) {
int bit = 1, digit = 1;
while (!(avail & bit)) {
++digit;
bit <<= 1;
}
avail ^= bit;
board[s.x][s.y] = '0' + digit;
return bit;
}
};