[LeetCode解題攻略] 37. Sudoku Solver

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

問題描述

編寫一個程式,求解數獨(Sudoku)問題。

數獨需要根據以下規則完成:

  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"]]

輸出:
[["5","3","4","6","7","8","9","1","2"]
,["6","7","2","1","9","5","3","4","8"]
,["1","9","8","3","4","2","5","6","7"]
,["8","5","9","7","6","1","4","2","3"]
,["4","2","6","8","5","3","7","9","1"]
,["7","1","3","9","2","4","8","5","6"]
,["9","6","1","5","3","7","2","8","4"]
,["2","8","7","4","1","9","6","3","5"]
,["3","4","5","2","8","6","1","7","9"]]
raw-image

解法一:回溯法

思路

回溯法(Backtracking),這是一種暴力解法。具體步驟如下:

  1. 從棋盤的左上角開始,找到第一個空格(.)。
  2. 嘗試將數字 1-9 填入這個位置,並檢查是否符合數獨規則。
  3. 如果符合,繼續檢查下一個空格;如果不符合,回退並嘗試下一個數字。
  4. 如果所有空格都被成功填充,則找到解。
  5. 如果嘗試所有數字後都無法填充當前空格,則回溯到上一個空格,並嘗試不同的數字。

程式碼

class Solution:
def solveSudoku(self, board: List[List[str]]) -> None:
"""
Do not return anything, modify board in-place instead.
"""

def is_valid(board, row, col, num):
num = str(num)
# 檢查行
for i in range(9):
if board[row][i] == num:
return False
# 檢查列
for i in range(9):
if board[i][col] == num:
return False
# 檢查子方格
box_row_start = (row // 3) * 3
box_col_start = (col // 3) * 3
for i in range(box_row_start, box_row_start + 3):
for j in range(box_col_start, box_col_start + 3):
if board[i][j] == num:
return False
return True

def backtrack(board):
for i in range(9):
for j in range(9):
if board[i][j] == '.':
for num in range(1, 10):
if is_valid(board, i, j, num):
board[i][j] = str(num)
if backtrack(board):
return True
# 回溯
board[i][j] = '.'
return False
return True

backtrack(board)

解釋

  1. is_valid 函數
    • 驗證某個數字是否可以填入指定位置。
    • 檢查行、列、以及子方格。
  2. backtrack 函數
    • 遍歷整個棋盤,找到第一個空格。
    • 嘗試填入 1-9,並遞歸解決。
    • 如果無法填入任何數字,回退到上一個空格並嘗試其他數字。
  3. 回溯步驟
    • 填入數字 → 檢查是否有效 → 遞歸到下一步。
    • 如果當前步驟無法進一步完成,將數字清除並嘗試下一個數字。

時間與空間複雜度

  • 時間複雜度:O(9^(n)),其中 n 是空格數量。
    • 每個空格最多嘗試 9 種數字,遞歸深度為空格數。
  • 空間複雜度:O(n),遞歸深度等於空格數。

解法二:優化回溯法

思路

我們可以通過額外的資料結構來加速檢查行、列和子方格是否有重複數字:

  1. 使用 3 個集合列表分別存儲每一行、每一列、以及每個子方格的數字。
  2. 當我們嘗試填入某個數字時,快速檢查集合是否包含該數字。

程式碼

class Solution:
def solveSudoku(self, board: List[List[str]]) -> None:
"""
Do not return anything, modify board in-place instead.
"""

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):
if board[i][j] != '.':
num = board[i][j]
rows[i].add(num)
cols[j].add(num)
boxes[(i // 3) * 3 + j // 3].add(num)

def backtrack(board):
for i in range(9):
for j in range(9):
if board[i][j] == '.':
for num in map(str, range(1, 10)):
box_index = (i // 3) * 3 + j // 3
if num not in rows[i] and num not in cols[j] and num not in boxes[box_index]:
# 填入數字
board[i][j] = num
rows[i].add(num)
cols[j].add(num)
boxes[box_index].add(num)

if backtrack(board):
return True

# 回溯
board[i][j] = '.'
rows[i].remove(num)
cols[j].remove(num)
boxes[box_index].remove(num)
return False
return True

backtrack(board)

解釋

  1. 優化檢查
    • 將每行、每列、每個子方格的數字存入集合。
    • 插入和檢查操作的時間複雜度為 O(1)。
  2. 回溯過程
    • 與解法一相同,但加入集合操作進行檢查,提升效率。

時間與空間複雜度

  • 時間複雜度:O(9^(n)),其中 n 是空格數量。
    • 優化的檢查操作不改變回溯的總體複雜度。
  • 空間複雜度:O(1)。
    • 集合大小固定為棋盤大小。

結論

  • 解法二通過使用集合加速檢查操作,使程式更高效且易於維護。
  • 整體回溯法對於數獨問題表現優秀,適用於求解這類組合問題。
留言
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
學習如何使用Python編寫一個數字猜謎遊戲,從中學習隨機數生成、使用者輸入、條件判斷和迴圈等程式設計基礎概念。
Thumbnail
學習如何使用Python編寫一個數字猜謎遊戲,從中學習隨機數生成、使用者輸入、條件判斷和迴圈等程式設計基礎概念。
Thumbnail
高中數學主題練習—解矩陣聯立方程組
Thumbnail
高中數學主題練習—解矩陣聯立方程組
Thumbnail
題目敘述 386. Lexicographical Numbers 給定一個數字n,請實作一個字典序(Lexical order)排列的報數機, 依字典序輸出所有1~n的數字。 你必須實現一個O(n) time線性時間,O(1) extra space常數額外空間的演算法。
Thumbnail
題目敘述 386. Lexicographical Numbers 給定一個數字n,請實作一個字典序(Lexical order)排列的報數機, 依字典序輸出所有1~n的數字。 你必須實現一個O(n) time線性時間,O(1) extra space常數額外空間的演算法。
Thumbnail
給定一個9x9的輸入陣列代表數獨題目已經 部分作答 的狀態, 請驗證已經作答的部分是否為合法的Sudoku的輸入。 註: 合法的Sudoku輸入必須滿足這些規則 1~9每一直排恰好出現一次。 1~9每一橫排恰好出現一次。 1~9在3x3的小方陣裏恰好出現一次。
Thumbnail
給定一個9x9的輸入陣列代表數獨題目已經 部分作答 的狀態, 請驗證已經作答的部分是否為合法的Sudoku的輸入。 註: 合法的Sudoku輸入必須滿足這些規則 1~9每一直排恰好出現一次。 1~9每一橫排恰好出現一次。 1~9在3x3的小方陣裏恰好出現一次。
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
1. 競賽題 或者 面試題 Medium Hard 以上題目,一開始在第一時間想不出解題思路,或者最佳解是很平常的事情。 通過官方解答、討論區的高手分享解題,進而學到新的解題思路,或者更泛用的演算法框架,就是最大的收穫。 2. 但是Easy分類的題目,通常就是該領域最基礎的題目,應該要
Thumbnail
1. 競賽題 或者 面試題 Medium Hard 以上題目,一開始在第一時間想不出解題思路,或者最佳解是很平常的事情。 通過官方解答、討論區的高手分享解題,進而學到新的解題思路,或者更泛用的演算法框架,就是最大的收穫。 2. 但是Easy分類的題目,通常就是該領域最基礎的題目,應該要
Thumbnail
題目會給我們一個排序好的陣列,還有一個目標值target 要求我們在陣列中尋找target所在的索引位置。 如果target 不存在,返回-1 題目要求必須在O( log n )對數時間內完成 。
Thumbnail
題目會給我們一個排序好的陣列,還有一個目標值target 要求我們在陣列中尋找target所在的索引位置。 如果target 不存在,返回-1 題目要求必須在O( log n )對數時間內完成 。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News