LeetCode:37. Sudoku Solver - Python

189 篇文章 3 订阅
151 篇文章 2 订阅

问题描述:

请编写一个程序,通过填充空单元格来解决数独问题。一个数独的解法需遵循如下规则:

(1)数字 1-9 在每一行只能出现一次。

(2)数字 1-9 在每一列只能出现一次。

(3)数字 1-9 在每一个以粗实线分隔3x3 宫格内只能出现一次。空白格用 '.' 表示。

  

图1. 左边一个数独,右边答案(答案被标记成红色

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

给定数独永远是 9x9 形式。

问题分析:(回溯法

     经典题目,采用回溯法递归实现(实现简单,不如直接用栈高效),基本思路,首先找到一个待填写的小方格9个数字依次试探填入,并行下一个方格,如果当前方格9个数字都不满足上面说的三个规则,那么恢复当前环境,并且回溯到前一个方格,依次进行,直至结束。有点像深度优先搜索哦。

Python3实现:

# 回溯法,递归实现
# @Time   :2018/6/20
# @Author :Yinxing

class Solution:
    def solveSudoku(self, board):
        self.board = board
        self.solve()

    def solve(self):  # 主递归函数
        row, col = self.findUnassigned()  # 获取一个未被分配的方格
        if row == -1 and col == -1:  # 没有找到,说明已经填好
            return True
        for num in ['1', '2', '3', '4', '5', '6', '7', '8', '9']:
            if self.isSafe(row, col, num):  # 循环填入数字,并判断是否满足要求
                self.board[row][col] = num
                if self.solve():  # 递归进入下一个方格
                    return True
                self.board[row][col] = '.'  # 后续不满足,那么现在要回复当前环境,并进行下一个数字试探
        return False

    def findUnassigned(self):  # 依次查找未被分配的方格
        for row in range(9):
            for col in range(9):
                if self.board[row][col] == '.':
                    return row, col
        return -1, -1

    def isSafe(self, row, col, ch):  # 判断是否当前方格填入的数字是否满足要求
        boxrow = row - row % 3  # 确定3x3小宫格的开始坐标,就是3x3小宫格第一个方格索引
        boxcol = col - col % 3
        if self.checkrow(row, ch) and self.checkcol(col, ch) and self.checksquare(boxrow, boxcol, ch):
            return True
        return False

    def checkrow(self, row, ch):  # 检查一行是否符合条件
        for col in range(9):
            if self.board[row][col] == ch:
                return False
        return True

    def checkcol(self, col, ch):  # 检查一列是否符合条件
        for row in range(9):
            if self.board[row][col] == ch:
                return False
        return True

    def checksquare(self, row, col, ch):  # 检查3x3小宫格是否符合条件
        for r in range(row, row + 3):
            for c in range(col, col + 3):
                if self.board[r][c] == ch:
                    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']]

    solu = Solution()
    solu.solveSudoku(board)

    for v in board:
        print(v)

程序还有很多地方可以优化,例如,在搜索可分配的方格时,每次进行了一次全局搜索,其实可以记录已经填写的方格,然后接着这个记录进行搜索,或者返回上一个。欢迎指正哦

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值