Leetcode 37:解数独

题目描述:
编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
示例:
在这里插入图片描述
输入: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”]]
输出:[[“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”]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:
在这里插入图片描述
提示:
board.length == 9
board[i].length == 9
board[i][j] 是一位数字或者 ‘.’
题目数据 保证 输入数独仅有一个解
思路:
按照「行优先」的顺序依次枚举每一个空白格中填的数字,通过**递归 + 回溯(剪枝)**的方法枚举所有可能的填法。当递归到最后一个空白格后,如果仍然没有冲突,说明我们找到了答案;在递归的过程中,如果当前的空白格不能填下任何一个数字,那么就进行回溯。

class Solution(object):
    def solveSudoku(self, board):
        """
        :type board: List[List[str]]
        :rtype: None Do not return anything, modify board in-place instead.
        """
        self.result = []
        self.slove(board)
        return self.result

    def slove(self, board):
        flag = True
        for i_c in range(9):
            for j_c in range(9):
                if board[i_c][j_c] == '.':
                    flag = False
        if flag:
            self.result = board
            return True
        f = False
        for i in range(9):
            for j in range(9):
                if board[i][j] == '.':
                    a = i
                    b = j
                    f = True
                    break
            if f:
                break
        # 测试19是否能填进去
        for k in range(1, 10):
            if self.isValid(board, a, b, k):
                board[a][b] = str(k)
                if self.slove(board):
                    return True
                else:
                    board[a][b] = '.'

    def isValid(self, board, a, b, k):
       # 检查行是否有一样的
        for h in range(9):
            if board[a][h] == str(k):
                return False
        # 检查列是否有一样的:
        for m in range(9):
            if board[m][b] == str(k):
                return False
        # 检查所在的小方块是否满足:
        g_a = int(a / 3)
        g_b = int(b / 3)
        set_g = set()
        for g_i in range(3):
            for g_j in range(3):
                if board[g_i+g_a*3][g_j+g_b*3] != '.':
                    if board[g_i+g_a*3][g_j+g_b*3] not in set_g:
                        set_g.add(board[g_i+g_a*3][g_j+g_b*3])
        if str(k) in set_g:
            return False
        return True

这里需要注意的是,同之前写剪枝的问题(N皇后)一样,在使用递归函数的时候,最好参数要重新生成一个这样避免结果一直被inplace的修改,或者inplace一个结果保存下来也是可以;但是在本问题中,因为要求的rtype中是要inplace的取结果且结果只有一个,所以这里比较有技巧的把递归函数的返回值定义成一个bool的类型,找到一个结果就结束了,不需要把所有的可能都试一遍,这样保证board就是结果。如果题目中,不要求是是inplace的到返回值,也可以对返回的结果进行拷贝(深拷贝)得到结果,同样的要是数独的解不是一个的时候,就需要每次深拷贝结果或者每次调用递归函数的时候换一块空间。

import copy


class Solution(object):
    def solveSudoku(self, board):
        """
        :type board: List[List[str]]
        :rtype: None Do not return anything, modify board in-place instead.
        """
        # 暴力填写,从第一个不为空的地方开始
        self.result = []
        self.slove(board)
        return self.result

    def slove(self, board):
        flag = True
        for i_c in range(9):
            for j_c in range(9):
                if board[i_c][j_c] == '.':
                    flag = False
        if flag:
            # print("找到解")
            self.result = copy.deepcopy(board)
            # print(board)
            return True
        f = False
        for i in range(9):
            for j in range(9):
                if board[i][j] == '.':
                    a = i
                    b = j
                    f = True
                    break
            if f:
                break
        # 测试19是否能填进去
        # print("a,b:", a, b)
        for k in range(1, 10):
            if self.isValid(board, a, b, k):
                board[a][b] = str(k)
                self.slove(board)
                board[a][b] = '.'


    def isValid(self, board, a, b, k):
       # 检查行是否有一样的
        for h in range(9):
            if board[a][h] == str(k):
                return False
        # 检查列是否有一样的:
        for m in range(9):
            if board[m][b] == str(k):
                return False
        # 检查所在的小方块是否满足:
        g_a = int(a / 3)
        g_b = int(b / 3)
        set_g = set()
        for g_i in range(3):
            for g_j in range(3):
                if board[g_i+g_a*3][g_j+g_b*3] != '.':
                    if board[g_i+g_a*3][g_j+g_b*3] not in set_g:
                        set_g.add(board[g_i+g_a*3][g_j+g_b*3])
        if str(k) in set_g:
            return False
        return True


if __name__ == '__main__':
    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"]]
    # board = [['5', '3', '4', '6', '7', '8', '9', '1', '2'], ['6', '7', '2', '1', '9', '5', '8', '3', '4'],
    #                ['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', '.', '.', '6'],
    #                ['.', '6', '.', '.', '.', '.', '2', '8', '.'], ['.', '.', '.', '4', '1', '9', '.', '.', '5'],
    #                ['.', '.', '.', '.', '8', '.', '.', '7', '9']]

    a = Solution()
    print(a.solveSudoku(board))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值