[LeetCode] 37. Sudoku Solver

Write a program to solve a Sudoku puzzle by filling the empty cells.

A sudoku solution must satisfy all of the following rules:

  1. Each of the digits 1-9 must occur exactly once in each row.
  2. Each of the digits 1-9 must occur exactly once in each column.
  3. Each of the digits 1-9 must occur exactly once in each of the 9 3x3 sub-boxes of the grid.

The '.' character indicates empty cells.

Example 1:

Input: board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
Output: [["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
Explanation: The input board is shown above and the only valid solution is shown below:

 

Constraints:

  • board.length == 9
  • board[i].length == 9
  • board[i][j] is a digit or '.'.
  • It is guaranteed that the input board has only one solution.

解数独。

编写一个程序,通过填充空格来解决数独问题。

数独的解法需 遵循如下规则:

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 '.' 表示。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/sudoku-solver
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

需要先做36题。做完36题,37题需要做的就是根据规则填充input。填充的规则是如果当前坐标上是一个点(.)的话,尝试放1-9之间的一个数字,放进去之后判断两件事,1是是否有效(这个数字在当前行/列/九宫格是否被放过了),2是放进去之后接着往下迭代处理剩下的部分的时候看看是否依然能解决完整个数独。所以这道题会牵涉到DFS回溯。

时间 - (9!)^9

空间O(1) - no extra space needed

Java实现

 1 class Solution {
 2     public void solveSudoku(char[][] board) {
 3         if (board == null || board.length == 0) {
 4             return;
 5         }
 6         helper(board);
 7     }
 8 
 9     private boolean helper(char[][] board) {
10         for (int i = 0; i < 9; i++) {
11             for (int j = 0; j < 9; j++) {
12                 if (board[i][j] == '.') {
13                     for (char c = '1'; c <= '9'; c++) {
14                         if (isValid(board, i, j, c)) {
15                             board[i][j] = c;
16                             if (helper(board)) {
17                                 return true;
18                             } else {
19                                 board[i][j] = '.';
20                             }
21                         }
22                     }
23                     return false;
24                 }
25             }
26         }
27         return true;
28     }
29 
30     private boolean isValid(char[][] board, int row, int col, char c) {
31         for (int i = 0; i < 9; i++) {
32             if (board[i][col] != '.' && board[i][col] == c) return false;
33             if (board[row][i] != '.' && board[row][i] == c) return false;
34             if (board[row / 3 * 3 + i / 3][col / 3 * 3 + i % 3] != '.' && board[row / 3 * 3 + i / 3][col / 3 * 3 + i % 3] == c) {
35                 return false;
36             }
37         }
38         return true;
39     }
40 }

 

2021年4月更新

我这里再提供一种更好理解的做法。首先我们还是创建三个boolean的二维数组记录已经存在的数字,然后我们需要一个helper函数去做backtracking,模拟完其他还不存在的数字。helper函数的退出条件是模拟到了最右下角的坐标;在没有模拟完毕之前,对于每个还没有填充过数字的坐标,我们都尝试去填充从1到9的每一个数字。

Java实现

 1 class Solution {
 2     public void solveSudoku(char[][] board) {
 3         // 三个布尔数组 表明 行, 列, 还有 3*3 的方格的数字是否被使用过
 4         boolean[][] rowUsed = new boolean[9][10];
 5         boolean[][] colUsed = new boolean[9][10];
 6         boolean[][][] boxUsed = new boolean[3][3][10];
 7         // 初始化
 8         for (int row = 0; row < board.length; row++) {
 9             for (int col = 0; col < board[0].length; col++) {
10                 int num = board[row][col] - '0';
11                 if (1 <= num && num <= 9) {
12                     rowUsed[row][num] = true;
13                     colUsed[col][num] = true;
14                     boxUsed[row / 3][col / 3][num] = true;
15                 }
16             }
17         }
18         // 递归尝试填充数组 
19         helper(board, rowUsed, colUsed, boxUsed, 0, 0);
20     }
21 
22     private boolean helper(char[][] board, boolean[][] rowUsed, boolean[][] colUsed, boolean[][][] boxUsed, int row,
23             int col) {
24         // 边界校验, 如果已经填充完成, 返回true, 表示一切结束
25         if (col == board[0].length) {
26             col = 0;
27             row++;
28             if (row == board.length) {
29                 return true;
30             }
31         }
32         // 是空则尝试填充, 否则跳过继续尝试填充下一个位置
33         if (board[row][col] == '.') {
34             // 尝试填充1~9
35             for (int num = 1; num <= 9; num++) {
36                 boolean canUsed = !(rowUsed[row][num] || colUsed[col][num] || boxUsed[row / 3][col / 3][num]);
37                 if (canUsed) {
38                     rowUsed[row][num] = true;
39                     colUsed[col][num] = true;
40                     boxUsed[row / 3][col / 3][num] = true;
41                     board[row][col] = (char) ('0' + num);
42                     if (helper(board, rowUsed, colUsed, boxUsed, row, col + 1)) {
43                         return true;
44                     }
45                     board[row][col] = '.';
46                     rowUsed[row][num] = false;
47                     colUsed[col][num] = false;
48                     boxUsed[row / 3][col / 3][num] = false;
49                 }
50             }
51         } else {
52             return helper(board, rowUsed, colUsed, boxUsed, row, col + 1);
53         }
54         return false;
55     }
56 }

 

相关题目

36. Valid Sudoku

37. Sudoku Solver

2133. Check if Every Row and Column Contains All Numbers

LeetCode 题目总结

posted @ 2020-04-20 06:39  CNoodle  阅读(437)  评论(0编辑  收藏  举报