LeetCode 36. 有效的數獨 | Python
36. 有效的數獨
題目來源:力扣(LeetCode)https://leetcode-cn.com/problems/valid-sudoku
題目
判斷一個 9x9 的數獨是否有效。只需要根據以下規則,驗證已經填入的數字是否有效即可。
數字 1-9 在每一行只能出現一次。
數字 1-9 在每一列只能出現一次。
數字 1-9 在每一個以粗實線分隔的 3x3 宮內只能出現一次。
上圖是一個部分填充的有效的數獨。
數獨部分空格內已填入了數字,空白格用 '.' 表示。
示例 1:
輸入: [ ["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"] ] 輸出: true
示例 2:
輸入: [ ["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"] ] 輸出: false 解釋: 除了第一行的第一個數字從 5 改為 8 以外,空格內其他數字均與 示例1 相同。 但由於位於左上角的 3x3 宮內有兩個 8 存在, 因此這個數獨是無效的。
說明:
- 一個有效的數獨(部分已被填充)不一定是可解的。
- 只需要根據以上規則,驗證已經填入的數字是否有效即可。
- 給定數獨序列只包含數字 1-9 和字元 '.' 。
- 給定數獨永遠是 9x9 形式的。
解題思路
思路:迭代,雜湊表
先看數獨有效的規則:
- 數字 1-9 在每一行只能出現一次。
- 數字 1-9 在每一列只能出現一次。
- 數字 1-9 在每一個以粗實線分隔的 3x3 宮內只能出現一次。
那麼我們可以根據這個規則,遍歷給定的數獨三次,分別去判斷每行,每列,每個 3x3 宮內是否有重複的數字。如果有重複的數字,可以直接返回 False;否則,直至遍歷結束,返回 True。
但是在這裡,可以考慮遍歷一次,同時去判斷每行,每列,每個 3x3 宮內是否有重複的數字。
在這裡,我們使用雜湊表去儲存每個數字出現的次數(包括每行,每列,每個 3x3 宮)。在這裡主要的問題是:因為數獨是 9x9 的,每行每列很容易列舉,但是列舉每個 3x3 宮格會有一些麻煩。
在這裡,列舉每個 3x3 宮,使用以下的式子:
box_index = (row // 3) * 3 + col // 3
根據上面的圖示,我們稍微說明一下,如何得到上面的式子。
先以列的角度看,標記為 0, 1, 2
的 3 個 3x3 宮格。可以看出這裡每個 3x3 宮格的索引更多取決於列索引。
先看標記為 0
的 3x3 宮格,這裡的列索引分別為 [0, 1, 2]
, 1
的 3x3 宮格,這裡的列索引分別為 [3, 4, 5]
, 2
的 3x3 宮格,這裡的列索引分別為 [6, 7, 8]
,也就是 col // 3
。
再以行的角度去看,標記為 0, 3, 6
的 3 個 3x3 宮格的索引跨度為 3,也就是說標記索引為 0
宮格如這樣得到的,0 * 3 + col // 3
,標記為 3
的宮格為:1 * 3 + col // 3
。
在這裡,上面公式的 [0, 1]
求得的公式與前面的類似,就是由 row // 3
獲得。那麼最終的式子為:
(row // 3) * 3 + col // 3
這裡稍微提一下,題目中說明,有效數獨並不一定可解,這裡不考慮是否可解,要求驗證的是已經填入的數字是否有效。
具體的演算法思路:
- 遍歷數獨,檢查數字在每行,每列,每個 3x3 宮格中是否有重複:
- 如果重複,返回 False
- 否則,記錄該數字,往下繼續遍歷
- 最終遍歷結束,返回 True。
以示例 2 為例,下面圖示表示演算法實現搜尋的過程:
具體的程式碼實現如下。
程式碼實現
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
# 定義雜湊表儲存數字出現在每行,每列,每個 3x3 的宮格的次數
# 題目中說明,給定的數獨一定是 9x9 的
rows = [{} for _ in range(9)]
cols = [{} for _ in range(9)]
boxes = [{} for _ in range(9)]
for i in range(9):
for j in range(9):
# 題目中要驗證是已經填入的數字,如果出現 '.' 表示留空,不作處理
if board[i][j] != '.':
# 取出數字,存入雜湊表中(需要轉換,給定的二維陣列數字是字串格式)
num = int(board[i][j])
rows[i][num] = rows[i].get(num, 0) + 1
cols[j][num] = cols[j].get(num, 0) + 1
# 代入列舉 3x3 宮格的公式
boxes[(i // 3) * 3 + j // 3][num] = boxes[(i // 3) * 3 + j // 3].get(num, 0) + 1
# 行,列,3x3 宮格,任意一個如果出現數字重複,則返回 False
if rows[i][num] > 1 or cols[j][num] > 1 or boxes[(i // 3) * 3 + j // 3][num] > 1:
return False
return True
實現結果
歡迎關注
公眾號 【書所集錄】