一. 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);
}
代码的时间复杂度为 O(9n) ,其中n为不确定的位置个数。
优化
虽然这样的解法能过LeetCode,但显然并不是一个特别好的解法,如果是我们自己去解决数独,当然不会从左到右依次遍历,找到空位就填。一般情况下我们都是去找能的填的数字比较少的空位来填,比如某个空位只能填一个数字,那么我们就会优先填那个空位。
因此,这道题最大的可以优化的地方就是,尽量降低搜索树前面结点分叉的数目(把分叉多的放到靠近叶结点的位置,把分叉少的放到靠近根结点的位置)。通过这样的处理,虽然时间复杂度是一样的,但是在解决实际问题的时候速度快了不是一点半点(无形中就减了很多枝)。
而且,从最少可填数目的空位开始填还可以避免冲突的产生,比如有同一行的两个空位,一个可以填3和4,一个只能填3,如果从前面的空位填3,那么后面那一个空位就无论如何也填不了了。而且如果在程序中不对这种情况进行检查的话,可能要等到搜索到很深的地方才能知道这个冲突。但如果是从可填数目最少的空位开始填,就绝对不会产生这种冲突(除非这个数独不可解)。