一. Sudoku Solver
Write a program to solve a Sudoku puzzle by filling the empty cells.
Empty cells are indicated by the character ‘.’.
You may assume that there will be only one unique solution.
A sudoku puzzle…
…and its solution numbers marked in red.
Difficulty:Hard
TIME:35MIN
解法
這道題似乎除了搜索之外,沒有特別好的解法,因此我直接寫了一個最普通的搜索(沒有任何優化),期待着會超時,然而並沒有。
搜索的思想當然很簡單,就是依次從左到右遍歷,發現一個可以填的空位,就選擇所有可以填的數字依次填入,當全部填好數字之後,就返回true,否則返回false。
bool dfs(vector<vector<char>>& board) {
vector<char> v(10, 0);
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (board[i][j] == '.') {
for (int k = 0; k < 9; k++) {
if (board[i][k] != '.')
v[board[i][k] - '0'] = 1;
if (board[k][j] != '.')
v[board[k][j] - '0'] = 1;
if (board[i / 3 * 3 + k / 3][j / 3 * 3 + k % 3] != '.')
v[board[i / 3 * 3 + k / 3][j / 3 * 3 + k % 3] - '0'] = 1;
}
for (int k = 1; k <= 9; k++) {
if (v[k] == 0) {
board[i][j] = k + '0';
if (dfs(board)) //如果數獨已經解決,就直接返回
return true;
board[i][j] = '.';
}
}
return false;
}
}
}
return true; //程序運行到這裏說明這個數獨已經解決了
}
void solveSudoku(vector<vector<char>>& board) {
dfs(board);
}
代碼的時間複雜度爲
優化
雖然這樣的解法能過LeetCode,但顯然並不是一個特別好的解法,如果是我們自己去解決數獨,當然不會從左到右依次遍歷,找到空位就填。一般情況下我們都是去找能的填的數字比較少的空位來填,比如某個空位只能填一個數字,那麼我們就會優先填那個空位。
因此,這道題最大的可以優化的地方就是,儘量降低搜索樹前面結點分叉的數目(把分叉多的放到靠近葉結點的位置,把分叉少的放到靠近根結點的位置)。通過這樣的處理,雖然時間複雜度是一樣的,但是在解決實際問題的時候速度快了不是一點半點(無形中就減了很多枝)。
而且,從最少可填數目的空位開始填還可以避免衝突的產生,比如有同一行的兩個空位,一個可以填3和4,一個只能填3,如果從前面的空位填3,那麼後面那一個空位就無論如何也填不了了。而且如果在程序中不對這種情況進行檢查的話,可能要等到搜索到很深的地方纔能知道這個衝突。但如果是從可填數目最少的空位開始填,就絕對不會產生這種衝突(除非這個數獨不可解)。