37.数独求解器
标签: backtracking, depth-first-search
难度: Hard
通过率: 63.52%
原题链接: https://leetcode.com/problems/sudoku-solver/description/
题目描述
编写一个程序,通过填充空白单元格来解决数独谜题。数独解决方案必须满足以下所有规则:
- 每行的数字1-9必须只出现一次。
 - 每列的数字1-9必须只出现一次。
 - 每个3x3的小方格内的数字1-9必须只出现一次。
 
字符.表示空单元格。
约束条件:
- 输入的board长度为9。
 - 每个board[i]的长度也是9。
 - board[i][j]是数字或
.。 - 输入保证只有一个解决方案。
 
解题思路
我们可以通过回溯法来解决数独问题。这种算法是一种试探法,即在搜索过程中不断尝试不同的数值组合,直到找到一个可 行的解决方案或遍历所有可能的组合。
解法步骤:
- 为空格填写数字:逐个检查每一个位置如果为空格,尝试在该位置填入数字1到9。
 - 验证可行性:每次填入一个数字后,检查这个数字是否符合数独规则(没有在当前行、列和3x3小方格中重复)。
 - 递归求解:如果填入某个数字后,依然满足规则,则递归求解下一个空白位置。
 - 回溯:若出现不满足规则的情况(无法找到可填入的数字),则回退到上一次决策位置,尝试其他数字。
 - 结束条件:当所有位置都成功填入数字时,整个数独被解出。
 
代码实现
- Python
 - C++
 - JavaScript
 - Java
 
def solveSudoku(board):
    def isValid(board, row, col, num):
        # 检查行是否有重复
        for x in range(9):
            if board[row][x] == num:
                return False
        # 检查列是否有重复
        for x in range(9):
            if board[x][col] == num:
                return False
        # 检查3x3小方格是否有重复
        startRow = row - row % 3
        startCol = col - col % 3
        for i in range(3):
            for j in range(3):
                if board[i + startRow][j + startCol] == num:
                    return False
        return True
    def solve(board):
        for i in range(9):
            for j in range(9):
                if board[i][j] == '.':
                    for num in '123456789':
                        if isValid(board, i, j, num):
                            board[i][j] = num
                            if solve(board):
                                return True
                            board[i][j] = '.'
                    return False
        return True
    solve(board)
    return board
class Solution {
public:
    bool isValid(vector<vector<char>>& board, int row, int col, char num) {
        for (int x = 0; x < 9; x++) {
            if (board[row][x] == num) return false;
            if (board[x][col] == num) return false;
            if (board[(row / 3) * 3 + x / 3][(col / 3) * 3 + x % 3] == num) return false;
        }
        return true;
    }
    bool solve(vector<vector<char>>& board) {
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] == '.') {
                    for (char num = '1'; num <= '9'; num++) {
                        if (isValid(board, i, j, num)) {
                            board[i][j] = num;
                            if (solve(board)) return true;
                            board[i][j] = '.';
                        }
                    }
                    return false;
                }
            }
        }
        return true;
    }
    void solveSudoku(vector<vector<char>>& board) {
        solve(board);
    }
};
const isValid = (board, row, col, num) => {
    for (let x = 0; x < 9; x++) {
        if (board[row][x] === num) return false;
        if (board[x][col] === num) return false;
        if (board[Math.floor(row / 3) * 3 + Math.floor(x / 3)][Math.floor(col / 3) * 3 + x % 3] === num) return false;
    }
    return true;
};
const solve = (board) => {
    for (let i = 0; i < 9; i++) {
        for (let j = 0; j < 9; j++) {
            if (board[i][j] === '.') {
                for (let num = '1'; num <= '9'; num++) {
                    if (isValid(board, i, j, num)) {
                        board[i][j] = num;
                        if (solve(board)) return true;
                        board[i][j] = '.';
                    }
                }
                return false;
            }
        }
    }
    return true;
};
const solveSudoku = (board) => {
    solve(board);
};
class Solution {
    private boolean isValid(char[][] board, int row, int col, char num) {
        for (int x = 0; x < 9; x++) {
            if (board[row][x] == num) return false;
            if (board[x][col] == num) return false;
            if (board[(row/3) * 3 + x/3][(col/3) * 3 + x%3] == num) return false;
        }
        return true;
    }
    private boolean solve(char[][] board) {
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] == '.') {
                    for (char num = '1'; num <= '9'; num++) {
                        if (isValid(board, i, j, num)) {
                            board[i][j] = num;
                            if (solve(board)) return true;
                            board[i][j] = '.';
                        }
                    }
                    return false;
                }
            }
        }
        return true;
    }
    public void solveSudoku(char[][] board) {
        solve(board);
    }
}
复杂度分析
- 时间复杂度:,因为最坏情况下,我们可能需要尝试9种选择为每个单元格填写数字,而一个数独板有81个单元格。
 - 空间复杂度:,即为数独板的大小,使得递归调用栈最深达到81层。