1. 程式人生 > >Leetcode演算法——36、判斷有效數獨

Leetcode演算法——36、判斷有效數獨

判斷一個 9*9 的數獨面板是否是有效的。

如果已經被填充的數字滿足以下條件,則說明是有效的:

  • 每一行只能包含無重複數字1-9
  • 每一列只能包含無重複數字1-9
  • 每一個 3*3 的子面板只能包含無重複數字1-9

備註:

  • 一個有效的數獨面板(部分填充)不必是可解的
  • 只要被填充的數字滿足有效條件即可
  • 數獨面板可以只被部分填充,其他空缺的為字元’.’
  • 面板只能包含數字1-9或者字元’.’
  • 面板大小總是 9*9

Example 1:
Input:
[
[“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”]
]
Output: true

Example 2:
Input:
[
[“8”,“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”]
]
Output: false
Explanation: Same as Example 1, except with the 5 in the top left corner being
modified to 8. Since there are two 8’s in the top left 3x3 sub-box, it is invalid.

思路

1、定義法

按照數獨的有效性定義,需要從行、列、3*3子塊三個方面都滿足數字的唯一性。

因此,可以分三個步驟,分別檢驗三個條件,只要有一個條件不滿足,則說明數獨無效。

1、檢驗每一行是否有重複數字。可以定義一個set,存放掃描過的數字,一旦新數字已經被包含在set中,則說明重複。
2、檢驗每一列是否有重複數字。與行同理。
3、檢驗每一個子塊是否有重複數字。需要檢查9個子塊。

2、一次檢驗法

上述方法是分別檢驗三個條件,每個條件都需要遍歷一遍所有的數字。

實際上,可以只通過一次遍歷,來同時判斷是否滿足三個條件。

維護一個 set,這個set中存放三種格式的元素:

  • (int i, char c):二元組,表示第 i 行中存在 c 字元。
  • (char c, int i):二元組,表示第 i 列中存在 c 字元。
  • (i, j, c):三元組,表示第 [i, j] 個子塊中存在 c 字元,其中 0 i , j 2 0\le i, j\le2

由於元組長度以及元素型別的不同,三種元素不會相互混合。

因此,每次掃描到一個數字,便生成 3 個對應的元組,分別判斷是否存在於 set 中,如果存在,則說明數獨無效;否則,將 3 個元組都加入到 set 中,繼續掃描下一個數字。

python實現

def isValidSudoku(board):
    """
    :type board: List[List[str]]
    :rtype: bool
    依次判斷3個有效條件是否全部滿足。
    """
    
    def is_row_valid(board):
        '''
        判斷一個數獨的每一行是否都包含無重複數字1-9
        '''
        for row in board:
            char_set = set()
            for char in row:
                if char != '.':
                    if char in char_set:
                        return False
                    char_set.add(char)
        return True
    
    # 1、每一行一定要包含無重複數字1-9
    if not is_row_valid(board):
        return False
                
    # 2、每一列一定要包含無重複數字1-9
    board_reverse = [[board[i][j] for i in range(9)] for j in range(9)]
    if not is_row_valid(board_reverse):
        return False    
    
    # 3、每一個 3*3 的子面板一定要包含無重複數字1-9
    for i in range(3):
        for j in range(3):
            char_set = set()
            for ii in range(i*3, (i+1)*3):
                for jj in range(j*3, (j+1)*3):
                    char = board[ii][jj]
                    if char != '.':
                        if char in char_set:
                            return False
                        char_set.add(char)
    return True

def isValidSudoku2(board):
    """
    :type board: List[List[str]]
    :rtype: bool
    一次遍歷,同時判斷是否滿足3個有效條件。
    """
    
    char_set = set()
    for i in range(9):
        for j in range(9):
            char = board[i][j]
            if char != '.':
                if (i, char) in char_set \
                    or (char, j) in char_set \
                    or (i//3, j//3, char) in char_set:
                        return False
                else:
                    char_set.add((i, char)) # 第i行的char字元
                    char_set.add((char, j))  # 第j列的char字元
                     # [i//3, j//3]位置的小面板的char字元
                    char_set.add((i//3, j//3, char))
    return True

if '__main__' == __name__:
    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"]
]
    print(isValidSudoku2(board))