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 字元,其中 。
由於元組長度以及元素型別的不同,三種元素不會相互混合。
因此,每次掃描到一個數字,便生成 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))