Write a program to solve a Sudoku puzzle by filling the empty cells.html
A sudoku solution must satisfy all of the following rules:git
1-9
must occur exactly once in each row.1-9
must occur exactly once in each column.1-9
must occur exactly once in each of the 9 3x3
sub-boxes of the grid.Empty cells are indicated by the character '.'
.github
A sudoku puzzle...数组
...and its solution numbers marked in red.函数
Note:post
1-9
and the character '.'
.9x9
.
这道求解数独的题是在以前那道 Valid Sudoku 的基础上的延伸,以前那道题让咱们验证给定的数组是否为数独数组,这道让求解数独数组,跟此题相似的有 Permutations,Combinations, N-Queens 等等,其中尤为是跟 N-Queens 的解题思路及其类似,对于每一个须要填数字的格子带入1到9,每代入一个数字都断定其是否合法,若是合法就继续下一次递归,结束时把数字设回 '.',判断新加入的数字是否合法时,只须要断定当前数字是否合法,不须要断定这个数组是否为数独数组,由于以前加进的数字都是合法的,这样可使程序更加高效一些,总体思路是这样的,可是实现起来能够有不一样的形式。一种实现形式是递归带上横纵坐标,因为是一行一行的填数字,且是从0行开始的,因此当i到达9的时候,说明全部的数字都成功的填入了,直接返回 ture。当j大于等于9时,当前行填完了,须要换到下一行继续填,则继续调用递归函数,横坐标带入 i+1。不然看若当前数字不为点,说明当前位置不须要填数字,则对右边的位置调用递归。若当前位置须要填数字,则应该尝试填入1到9内的全部数字,让c从1遍历到9,每当试着填入一个数字,都须要检验是否有冲突,使用另外一个子函数 isValid 来检验是否合法,假如不合法,则跳过当前数字。若合法,则将当前位置赋值为这个数字,并对右边位置调用递归,若递归函数返回 true,则说明能够成功填充,直接返回 true。不行的话,须要重置状态,将当前位置恢复为点。若全部数字都尝试了,仍是不行,则最终返回 false。检测当前数组是否合法的原理跟以前那道 Valid Sudoku 很是的类似,但更简单一些,由于这里只须要检测新加入的这个数字是否会跟其余位置引发冲突,分别检测新加入数字的行列和所在的小区间内是否有重复的数字便可,参见代码以下:url
解法一:spa
class Solution { public: void solveSudoku(vector<vector<char>>& board) { helper(board, 0, 0); } bool helper(vector<vector<char>>& board, int i, int j) { if (i == 9) return true; if (j >= 9) return helper(board, i + 1, 0); if (board[i][j] != '.') return helper(board, i, j + 1); for (char c = '1'; c <= '9'; ++c) { if (!isValid(board, i , j, c)) continue; board[i][j] = c; if (helper(board, i, j + 1)) return true; board[i][j] = '.'; } return false; } bool isValid(vector<vector<char>>& board, int i, int j, char val) { for (int x = 0; x < 9; ++x) { if (board[x][j] == val) return false; } for (int y = 0; y < 9; ++y) { if (board[i][y] == val) return false; } int row = i - i % 3, col = j - j % 3; for (int x = 0; x < 3; ++x) { for (int y = 0; y < 3; ++y) { if (board[x + row][y + col] == val) return false; } } return true; } };
还有另外一种递归的写法,这里就不带横纵坐标参数进去,因为递归须要有 boolean 型的返回值,因此不能使用原函数。由于没有横纵坐标,因此每次遍历都须要从开头0的位置开始,这样无形中就有了大量的重复检测,致使这种解法虽然写法简洁一些,但击败率是没有上面的解法高的。这里的检测数组冲突的子函数写法也比上面简洁很多,只用了一个 for 循环,用来同时检测行列和小区间是否有冲突,注意正确的坐标转换便可,参见代码以下:code
解法二:htm
class Solution { public: void solveSudoku(vector<vector<char>>& board) { helper(board); } bool helper(vector<vector<char>>& board) { for (int i = 0; i < 9; ++i) { for (int j = 0; j < 9; ++j) { if (board[i][j] != '.') continue; for (char c = '1'; c <= '9'; ++c) { if (!isValid(board, i, j, c)) continue; board[i][j] = c; if (helper(board)) return true; board[i][j] = '.'; } return false; } } return true; } bool isValid(vector<vector<char>>& board, int i, int j, char val) { for (int k = 0; k < 9; ++k) { if (board[k][j] != '.' && board[k][j] == val) return false; if (board[i][k] != '.' && board[i][k] == val) return false; int row = i / 3 * 3 + k / 3, col = j / 3 * 3 + k % 3; if (board[row][col] != '.' && board[row][col] == val) return false; } return true; } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/37
相似题目:
Unique Paths III
参考资料:
https://leetcode.com/problems/sudoku-solver/
https://leetcode.com/problems/sudoku-solver/discuss/15853/Simple-and-Clean-Solution-C%2B%2B