[LeetCode in Python] 37 (H) sudoku solver 解数独

题目

https://leetcode-cn.com/problems/sudoku-solver/

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

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

数字1-9在每一行只能出现一次。
数字1-9在每一列只能出现一次。
数字1-9在每一个以粗实线分隔的3x3宫内只能出现一次。

空白格用 '.' 表示。

一个数独。

答案被标成红色。

Note:

给定的数独序列只包含数字 1-9 和字符 '.' 。
你可以假设给定的数独只有唯一解。
给定数独永远是 9x9 形式的。

解题思路

  • DFS + Backtracking
  • 使用3个列表存储行、列、格的已有数字状态。
  • 为了代码精简,将几个常用逻辑提炼为函数了。
  • 先根据当前情况,初始化3个列表。
  • 然后是标准回溯框架:
    • 一进来先判断是否到底了,
    • 然后得出下一步的坐标,
    • 如果当前位置有数字,在下一步坐标上递归调用backtracking函数
    • 如果当前位置是空白,那么循环遍历1-9
      • 先看该数字是否违和
      • 如果OK,就更新3个列表,并在数独表格中设置该数字
      • 然后在下一步坐标上递归调用backtracking函数
      • 如果返回False,则恢复3个列表,并擦掉数独表格中填入的数字
    • 到此返回False
  • 主程序入口:backtracking(0,0)

代码

class Solution:
    def solveSudoku(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """

        row_list = [[False]*10 for _ in range(9)]
        col_list = [[False]*10 for _ in range(9)]
        grid_list = [[False]*10 for _ in range(9)]

        # - get grid index
        def get_grid_index(row,col):
            return (row//3)*3 + col//3

        # - set state to value (True/False)
        def set_state(row, col, num, value):
            row_list[row][num] = value
            col_list[col][num] = value
            grid_list[get_grid_index(row, col)][num] = value

        # - init state
        for row in range(9):
            for col in range(9):
                c = board[row][col]
                if c != '.': 
                    set_state(row, col, int(c), True)

        # - check if num is valid at (row, col)
        def is_valid(row, col, num):
            return not any([
                row_list[row][num],
                col_list[col][num],
                grid_list[get_grid_index(row,col)][num]
            ])

        # - backtracking
        def backtracking(row, col):
            # - exit if out of boundary
            if row == 9:
                return True

            # - get next position
            next_col = (col + 1) % 9
            next_row = row+1 if next_col==0 else row

            # - only expand for '.'
            c = board[row][col]
            if c != '.': 
                return backtracking(next_row, next_col)

            # - try 1..9 
            for i in range(1,10):
                if not is_valid(row, col, i): continue

                # - update state
                set_state(row, col, i, True)
                board[row][col] = '%s' % i

                # - go to next pos
                if backtracking(next_row, next_col): 
                    return True
                
                # - restore state
                set_state(row, col, i, False)
                board[row][col] = '.'
                
            return False

        # - backtracking from (0,0)
        backtracking(0,0)
原文地址:https://www.cnblogs.com/journeyonmyway/p/12694063.html