[LeetCode解題攻略] 36. Valid Sudoku

更新於 發佈於 閱讀時間約 11 分鐘

問題描述

給定一個 9x9 的數獨棋盤,請驗證這個棋盤是否有效。

數獨棋盤需滿足以下條件:

  1. 每一行只能包含數字 1-9,不能重複。
  2. 每一列只能包含數字 1-9,不能重複。
  3. 每一個 3x3 的子方格只能包含數字 1-9,不能重複。

注意:

  • 數獨棋盤中空格由 '.' 表示。
  • 不需要檢查是否可以解出這個數獨,只需要驗證數獨是否有效。


範例 1

raw-image
輸入:
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"]]
輸出:true

範例 2

輸入:
board =
[["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
解釋:第一列有兩個 8,因此這個棋盤是無效的。

解法一:使用集合檢查每一行、每一列和每個子方格

思路

我們可以將數獨棋盤視為三部分來驗證:

  1. 每一行是否有重複數字。
  2. 每一列是否有重複數字。
  3. 每一個 3x3 子方格是否有重複數字。

對於每部分,我們可以使用 集合 來檢查是否有重複數字:

  • 如果某數字已經存在於集合中,則表示有重複,棋盤無效。
  • 如果某位置是 '.',則忽略。

程式碼

class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
rows = [set() for _ in range(9)]
cols = [set() for _ in range(9)]
boxes = [set() for _ in range(9)]

for i in range(9):
for j in range(9):
num = board[i][j]
if num == '.':
continue

# 檢查行
if num in rows[i]:
return False
rows[i].add(num)

# 檢查列
if num in cols[j]:
return False
cols[j].add(num)

# 檢查子方格
box_index = (i // 3) * 3 + (j // 3)
if num in boxes[box_index]:
return False
boxes[box_index].add(num)

return True

時間與空間複雜度

  • 時間複雜度:O(1)
    數獨棋盤大小固定為 9x9,因此無論如何最多遍歷 81 個格子,時間複雜度為常數。
  • 空間複雜度:O(1)
    使用了三個長度為 9 的集合來存儲行、列和子方格的數字,總空間需求固定。

解法二:壓縮空間版本

思路

我們可以用 唯一標識符 來壓縮存儲需求,僅用一個集合來記錄每個數字是否出現過。

  1. 將每個數字出現的條件轉化為字串標識,例如:
    • 數字 5 出現在第 0 行,標識為 "5 in row 0"
    • 數字 5 出現在第 1 列,標識為 "5 in col 1"
    • 數字 5 出現在子方格 (0,0),標識為 "5 in box 0-0"
  2. 將這些標識存入集合,如果發現重複標識則返回 False

程式碼

class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
seen = set()
for i in range(9):
for j in range(9):
num = board[i][j]
if num == '.':
continue

row_key = f"{num} in row {i}"
col_key = f"{num} in col {j}"
box_key = f"{num} in box {i//3}-{j//3}"

if row_key in seen or col_key in seen or box_key in seen:
return False

seen.add(row_key)
seen.add(col_key)
seen.add(box_key)

return True

時間與空間複雜度

  • 時間複雜度:O(1)
    同樣固定遍歷 81 個格子。
  • 空間複雜度:O(1)
    由於最多存儲 81 條標識(每個數字最多三條標識),空間需求仍然是固定的。

解法三:逐步檢查行、列、子方格

思路

  1. 檢查每一行:將每一行的數字存入集合,檢查是否有重複。
  2. 檢查每一列:同樣將每一列的數字存入集合,檢查是否有重複。
  3. 檢查每個子方格:對每個子方格中的數字進行檢查。

這種方法較為直觀,分開處理每部分。

程式碼

class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
def isValidSet(values):
nums = [v for v in values if v != '.']
return len(nums) == len(set(nums))

# 檢查行
for row in board:
if not isValidSet(row):
return False

# 檢查列
for col in zip(*board):
if not isValidSet(col):
return False

# 檢查子方格
for i in range(0, 9, 3):
for j in range(0, 9, 3):
box = [board[x][y] for x in range(i, i + 3) for y in range(j, j + 3)]
if not isValidSet(box):
return False

return True

時間與空間複雜度

  • 時間複雜度:O(1)
    固定遍歷 81 個格子,且每次檢查集合操作為常數時間。
  • 空間複雜度:O(1)
    每次檢查時僅臨時存儲數字,無額外存儲需求。

結論

  1. 最佳解法解法二(壓縮空間版本),僅用一個集合,實現簡潔且高效。
  2. 其他解法:對於初學者,解法一解法三 更容易理解。
  3. 此題重點在於如何高效檢查每一行、每一列與每個子方格是否符合規範,並通過集合實現唯一性檢查。
留言
avatar-img
留言分享你的想法!
avatar-img
追極光的北極熊|軟體工程師的小天地
12會員
163內容數
歡迎來到我的部落格!這裡記錄了軟體工程師的日常生活點滴,並分享程式設計與演算法的實用教學。無論你是初學者還是有經驗的開發者,都能在這裡找到深入淺出的技術解析與實戰技巧。此外,我也會分享工作中的心路歷程與學習心得,讓你不僅學到技術,更能瞭解軟體開發的實際應用與挑戰。希望透過這個平台,能與你共同成長,激發對技術的熱情!
2025/04/04
給定一個非負整數陣列 nums,其中 nums[i] 代表你在索引 i 處最多可以向右跳幾步。 請判斷是否能夠從索引 0 跳到最後一個索引。
2025/04/04
給定一個非負整數陣列 nums,其中 nums[i] 代表你在索引 i 處最多可以向右跳幾步。 請判斷是否能夠從索引 0 跳到最後一個索引。
2025/03/28
給定一個 m x n 的矩陣 matrix,請按照 螺旋順序(spiral order) 返回矩陣中的所有元素。
2025/03/28
給定一個 m x n 的矩陣 matrix,請按照 螺旋順序(spiral order) 返回矩陣中的所有元素。
2025/03/20
這道題是 LeetCode 的經典題之一,要求我們找出 一個子陣列,使其和最大,並返回這個最大和的數值。
2025/03/20
這道題是 LeetCode 的經典題之一,要求我們找出 一個子陣列,使其和最大,並返回這個最大和的數值。
看更多
你可能也想看
Thumbnail
常常被朋友問「哪裡買的?」嗎?透過蝦皮分潤計畫,把日常購物的分享多加一個步驟,就能轉換成現金回饋。門檻低、申請簡單,特別適合學生與上班族,讓零碎時間也能創造小確幸。
Thumbnail
常常被朋友問「哪裡買的?」嗎?透過蝦皮分潤計畫,把日常購物的分享多加一個步驟,就能轉換成現金回饋。門檻低、申請簡單,特別適合學生與上班族,讓零碎時間也能創造小確幸。
Thumbnail
嗨!歡迎來到 vocus vocus 方格子是台灣最大的內容創作與知識變現平台,並且計畫持續拓展東南亞等等國際市場。我們致力於打造讓創作者能夠自由發表、累積影響力並獲得實質收益的創作生態圈!「創作至上」是我們的核心價值,我們致力於透過平台功能與服務,賦予創作者更多的可能。 vocus 平台匯聚了
Thumbnail
嗨!歡迎來到 vocus vocus 方格子是台灣最大的內容創作與知識變現平台,並且計畫持續拓展東南亞等等國際市場。我們致力於打造讓創作者能夠自由發表、累積影響力並獲得實質收益的創作生態圈!「創作至上」是我們的核心價值,我們致力於透過平台功能與服務,賦予創作者更多的可能。 vocus 平台匯聚了
Thumbnail
給定一個9x9的輸入陣列代表數獨題目已經 部分作答 的狀態, 請驗證已經作答的部分是否為合法的Sudoku的輸入。 註: 合法的Sudoku輸入必須滿足這些規則 1~9每一直排恰好出現一次。 1~9每一橫排恰好出現一次。 1~9在3x3的小方陣裏恰好出現一次。
Thumbnail
給定一個9x9的輸入陣列代表數獨題目已經 部分作答 的狀態, 請驗證已經作答的部分是否為合法的Sudoku的輸入。 註: 合法的Sudoku輸入必須滿足這些規則 1~9每一直排恰好出現一次。 1~9每一橫排恰好出現一次。 1~9在3x3的小方陣裏恰好出現一次。
Thumbnail
給定一個整數陣列hand代表手牌點數,和參數groupSize。請問能不能每groupSize牌一組,每一組都拼出順子? 如果可以,返回True。如果無解,返回False。演算法使用最小堆積或排序。關鍵知識點:從小到大掃描每張牌,檢查能不能組成牌組長度為groupSize的順子即可。
Thumbnail
給定一個整數陣列hand代表手牌點數,和參數groupSize。請問能不能每groupSize牌一組,每一組都拼出順子? 如果可以,返回True。如果無解,返回False。演算法使用最小堆積或排序。關鍵知識點:從小到大掃描每張牌,檢查能不能組成牌組長度為groupSize的順子即可。
Thumbnail
題目敘述 題目會給我們一個陣列,要求我們返回 兩數之和=target所在的陣列索引值。 題目還額外保證,一定剛好有一組解。 測試範例 Example 1: Input: nums = [2,7,11,15], target = 9 Output: [0,1]
Thumbnail
題目敘述 題目會給我們一個陣列,要求我們返回 兩數之和=target所在的陣列索引值。 題目還額外保證,一定剛好有一組解。 測試範例 Example 1: Input: nums = [2,7,11,15], target = 9 Output: [0,1]
Thumbnail
題目會給我們節點總數目n、左子樹的關係陣列、右子樹的關係陣列,要求我們驗證在給定的條件下,能不能構成一顆合法的二元樹。
Thumbnail
題目會給我們節點總數目n、左子樹的關係陣列、右子樹的關係陣列,要求我們驗證在給定的條件下,能不能構成一顆合法的二元樹。
Thumbnail
Leetcode #101 Symmetric Tree 題目會給定一顆樹,要求我們判定這棵樹是不是左右鏡像對稱(Symmetric)。
Thumbnail
Leetcode #101 Symmetric Tree 題目會給定一顆樹,要求我們判定這棵樹是不是左右鏡像對稱(Symmetric)。
Thumbnail
本遊戲為李國賢老師所研發,已經出版專書一套四冊。若你對此遊戲或數學有興趣,請跟我聯繫:ligosan2008@gmail.com。
Thumbnail
本遊戲為李國賢老師所研發,已經出版專書一套四冊。若你對此遊戲或數學有興趣,請跟我聯繫:ligosan2008@gmail.com。
Thumbnail
連續悶蛋了幾天,這天的與其說有趣,不如說因為我成功用推理,省去大多數人都要盲試的步驟,所以拿來跟大家分享一下。 如果只要WIN GAME,以這麼大路的格式不太可能會輸,但這個也許可以三行完成。 窮舉完成, 如果要退位則無實解。
Thumbnail
連續悶蛋了幾天,這天的與其說有趣,不如說因為我成功用推理,省去大多數人都要盲試的步驟,所以拿來跟大家分享一下。 如果只要WIN GAME,以這麼大路的格式不太可能會輸,但這個也許可以三行完成。 窮舉完成, 如果要退位則無實解。
Thumbnail
題目敘述 撲克牌有4種花色,分別是黑桃、紅心、方塊跟梅花 其中每一中花色都有一個數字,範圍從1~13,總共13*4 = 52張牌 1) 請從52張牌中挑選13張牌,輸出第一列所示,牌跟牌之間用tab隔開。 2) 請將抽出的13張牌依數字由大至小排序,同樣數字下比花色。
Thumbnail
題目敘述 撲克牌有4種花色,分別是黑桃、紅心、方塊跟梅花 其中每一中花色都有一個數字,範圍從1~13,總共13*4 = 52張牌 1) 請從52張牌中挑選13張牌,輸出第一列所示,牌跟牌之間用tab隔開。 2) 請將抽出的13張牌依數字由大至小排序,同樣數字下比花色。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News