【leetcode】解数独

topic

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

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

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

空白格用 ‘.’ 表示。

此处输入图片的描述
一个数独。

此处输入图片的描述

答案被标成红色。

Note:

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

思路

给定一个9x9数组board,先判断此数独是否有效或有解,若无解则打印“This is an invalid sudoku!”,若有解则打印“This is a valid sudoku!”,并解此数独打印结果。

1. 判断数独是否有效

定义一个函数,判断此数独是否有解:isValidSudoku(),输入参数是9x9的二维数组,返回值是True或False。函数体中依次判断此数独是否满足数独的三条解法规则。

第一条,数字 1-9 在每一行只能出现一次:依次取出board中每行数据i,用列表生成式去除列表i中所有的‘.’得到列表r,再将列表i变成集合去重,去除元素‘.’,得到i中所有非‘.’的元素集合row,若r与row长度相同说明该行中数字元素不重复,否则说明i中有重复的数字元素,一旦出现重复数字,此数独无效,直接返回False。

    for i in board:
        r=[j for j in i if j!='.']
        row=set(i)-{'.'}
        if len(r)!=len(row):
            return False

第二条,数字 1-9 在每一列只能出现一次:先遍历列表board,用列表生成式取出每列数据的列表temp,与第一条一样,除去temp中‘.’得到列表col,对列表temp去重,再删去元素‘.’得到集合c,比较集合c和列表col的长度,若长度相同说明该列数据无重复数字元素,若长度不同则违反了条件二,直接返回False。

    for i in range(9):
        temp=[board[j][i] for j in range(9)]
        col=[j for j in temp if j!='.']
        c=set(temp)-{'.'}
        if len(c)!=len(col):
            #print('col false:',i)
            return False

第三条,数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次:首先要取出每个3x3宫内的数据,采用列表切片取出三行每行三列的数据(首行首列位置由range(0,9,3)给出),每宫的数据存为列表temp,然后同前两条规则一样,去除‘.’的列表unit和去重的集合u比较长度,判断是否有重复数字。

    for i in range(0,9,3):
        for j in range(0,9,3):
            temp = board[i][j:j + 3] + board[i + 1][j:j + 3] + board[i + 2][j:j + 3]
            unit = [i for i in temp if i!='.']
            u=set(temp)-{'.'}
            if len(unit)!=len(u):
                return False
    return True

数独中所有行、列及3x3宫内数据全部满足以上三条规则,则判定此数独有效,返回True。

2. 解数独

定义解数独的函数solveSudoku(board),输入参数是待解的数独二维数组board,解数独的思路如下:

①首先找到数独中的第一个‘.’:如果找不到‘.’,说明已解完此数独,返回结果board即可(递归的基例);如果能找到,记下第一个‘.’的行列坐标(row,col),此位置为本次解的目标位置。

def solveSudoku(board):
    #①find position of the first '.'
    row,col=find_empty(board)
    if row==None:
        return board

定义一个函数,寻找第一个‘.’:find_empty()。输入参数为待解数独二维数组board,返回值为寻找到的第一个‘.’的行列索引(索引值0~8);当board中不存在‘.’时,返回两个None,代码如下:

def find_empty(board):
    for r in range(9):
        for c in range(9):
            if board[r][c]=='.':
                return r,c
    else:
        return None,None

②然后根据三条规则依次算出目标位置(row,col)的可行解集合key1、key2、key3,对这三个可行解集合求交集得到本轮可行解集合key。

    #②find feasible key for the position
    key1={'1','2','3','4','5','6','7','8','9'}-set(board[row])
    key2={'1','2','3','4','5','6','7','8','9'}-set([board[i][col] for i in range(9)])
    key3=find_key3(board,row,col)
    key=key1&key2&key3

③最后进行递归操作:
若key为空,说明此情况无解,直接返回False;
若key中有值,弹出一个填入目标位置,对填入一个可行解后的新数独进行下一轮的解,若下一轮解数独有返回值,则此返回值即为此数独的最终解,直接返回该结果即可;若下一轮解数独返回False,说明本次填入的解不对,继续从key中pop出其他可行解进行递归,直至得到最终结果。

    #③recursion
    while key:
        k = key.pop()
        board[row][col]=k
        temp=solveSudoku(board)
        if temp:
            return temp
        else:
            board[row][col] = '.'
    else:
        return False

代码

def find_key3(board,row,col):
    r=row//3*3
    c=col//3*3
    temp=board[r][c:c+3]+board[r+1][c:c+3]+board[r+2][c:c+3]
    key3={'1','2','3','4','5','6','7','8','9'}-set(temp)
    return key3
def find_empty(board):
    for r in range(9):
        for c in range(9):
            if board[r][c]=='.':
                return True,r,c
    else:
        return False,board,None
def solveSudoku(board):
    """
    :type board: List[List[str]]
    :rtype: void Do not return anything, modify board in-place instead.
    """
    #①find position of the first '.'
    row,col=find_empty(board)
    if row==None:
        return board
    #②find feasible key for the position
    key1={'1','2','3','4','5','6','7','8','9'}-set(board[row])
    key2={'1','2','3','4','5','6','7','8','9'}-set([board[i][col] for i in range(9)])
    key3=find_key3(board,row,col)
    key=key1&key2&key3
    #③recursion
    while key:
        k = key.pop()
        board[row][col]=k
        temp=solveSudoku(board)
        if temp:
            return temp
        else:
            board[row][col] = '.'
    else:
        return False
def isValidSudoku(board):
    """
    :type board: List[List[str]]
    :rtype: bool
    """
    for i in board:
        r=[j for j in i if j!='.']
        row=set(i)-{'.'}
        if len(r)!=len(row):
            return False

    for i in range(9):
        temp=[board[j][i] for j in range(9)]
        col=[j for j in temp if j!='.']
        c=set(temp)-{'.'}
        if len(c)!=len(col):
            return False
    for i in range(0,9,3):
        for j in range(0,9,3):
            temp = board[i][j:j + 3] + board[i + 1][j:j + 3] + board[i + 2][j:j + 3]
            unit = [i for i in temp if i!='.']
            u=set(temp)-{'.'}
            if len(unit)!=len(u):
                return False
    return True

def 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"]]
    for i in board:
        print(i)
    if isValidSudoku(board):
        print('This is a valid sudoku! \nThe sudoku solution is as follow:\n')
        key = solveSudoku(board)
        for i in key:
            print(i)
    else:
        print('This is an invalid sudoku!')


  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值