[LeetCode解題攻略] 51. N-Queens

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

題目描述

51. N-Queens 是一個經典的演算法題目,要求在 n x n 的棋盤上放置 n 個皇后,並且保證它們之間不會互相攻擊。皇后可以攻擊任意與它處於同一行、同一列或同一對角線的其他棋子,因此我們需要找到所有合法的放置方式,並返回所有解。

題目要求

  • 輸入:一個整數 n,表示棋盤的大小(即 n x n 的棋盤)。
  • 輸出:所有可能的放置方式,每種方式應以 n 個字符串的列表表示,每個字符串的長度為 n,並且字符 Q 表示皇后,字符 . 表示空格。

範例

範例 1

輸入:n = 4
輸出:
[
[".Q..", // 解 1
"...Q",
"Q...",
"..Q."],

["..Q.", // 解 2
"Q...",
"...Q",
".Q.."]
]
raw-image


範例 2

輸入:n = 1
輸出:
[
["Q"]
]

解題思路

這個問題是一個經典的回溯法(Backtracking)問題。問題的核心是從每一行開始,逐行嘗試放置皇后,並檢查當前放置是否合法。如果合法,就繼續放置下一個皇后;如果不合法,則退回上一行進行重試。

關鍵觀念:

  1. 行與列:每一行只能放置一個皇后,這樣可以保證不會有皇后處於同一行。
  2. 對角線:每個皇后有兩條對角線,這些對角線必須避免與其他皇后重疊。
  3. 回溯法:我們使用回溯法來不斷嘗試放置每一行的皇后,並且在每一次嘗試後進行合法性檢查。如果不合法,就回到上一步重新嘗試。

解法一:回溯法

思路:

  1. 我們使用一個長度為 n 的數組 board,其中 board[i] 表示第 i 行放置的皇后所處的列數。
  2. 每次嘗試放置一個皇后時,我們檢查是否會與前面的皇后攻擊,這就是回溯的核心。
  3. 一旦我們成功放置了 n 個皇后,就找到了一个解。

程式碼實作:

class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
def is_valid(board, row, col):
for i in range(row):
# 檢查是否在同一列或者對角線上
if board[i] == col or abs(board[i] - col) == abs(i - row):
return False
return True

def backtrack(board, row):
if row == n:
result.append(["." * i + "Q" + "." * (n - i - 1) for i in board])
return
for col in range(n):
if is_valid(board, row, col):
board[row] = col
backtrack(board, row + 1)
board[row] = -1 # Backtrack

result = []
board = [-1] * n # 初始化棋盤
backtrack(board, 0)
return result

程式碼說明:

  • is_valid(board, row, col):這個函數用來檢查在第 row 行第 col 列放置皇后是否合法。它會檢查該列是否已經有皇后,還會檢查對角線的衝突。
  • backtrack(board, row):回溯的核心函數。它逐行放置皇后,每放置一個皇后就檢查是否合法。如果成功放置 n 個皇后,將當前棋盤的解加入 result。如果放置過程中發現衝突,則進行回溯。
  • result:存儲所有合法的棋盤配置,並以字符串的形式輸出。

時間與空間複雜度:

  • 時間複雜度:最壞情況下,對於每一行,我們都有 n 個可能的位置來嘗試放置皇后,且每次檢查是否合法是 O(n)。因此時間複雜度是 O(n!)
  • 空間複雜度:由於我們使用回溯法,空間複雜度是 O(n),主要來自於遞歸的調用堆疊。

解法二:剪枝優化

在解法一中,我們檢查每一列和對角線是否有衝突。這個檢查是逐一進行的,效率並不高。我們可以進一步優化,通過三個集合來追蹤列和兩條對角線的佔用情況。

程式碼實作:

class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
def backtrack(row):
if row == n:
result.append(["." * col + "Q" + "." * (n - col - 1) for col in board])
return
for col in range(n):
if col in cols or (row - col) in diag1 or (row + col) in diag2:
continue
cols.add(col)
diag1.add(row - col)
diag2.add(row + col)
board[row] = col
backtrack(row + 1)
cols.remove(col)
diag1.remove(row - col)
diag2.remove(row + col)

result = []
board = [-1] * n
cols = set()
diag1 = set()
diag2 = set()
backtrack(0)
return result

程式碼說明:

  • cols, diag1, diag2:這些集合用來記錄被佔用的列和對角線,從而避免重複檢查每個位置。
  • 這種方法減少了每次放置皇后時的檢查時間,使得算法更高效。

時間與空間複雜度:

  • 時間複雜度:與回溯法相同,最壞情況下是 O(n!),但由於優化了衝突檢查,實際情況會比解法一更好一點。
  • 空間複雜度:空間複雜度是 O(n),來自於遞歸調用堆疊及存儲已經佔用的列和對角線集合。

結論

這道 N-Queens 題目是一個典型的回溯法問題,通過遞歸和回溯來嘗試不同的解,並檢查合法性。這題可以幫助大家更好地理解回溯法的使用,並且能夠進一步掌握如何進行優化。回溯法的解法雖然直觀,但是在大規模數據上依然有效,對於許多組合問題有廣泛應用。


留言
avatar-img
留言分享你的想法!
avatar-img
追極光的北極熊|軟體工程師的小天地
10會員
162內容數
歡迎來到我的部落格!這裡記錄了軟體工程師的日常生活點滴,並分享程式設計與演算法的實用教學。無論你是初學者還是有經驗的開發者,都能在這裡找到深入淺出的技術解析與實戰技巧。此外,我也會分享工作中的心路歷程與學習心得,讓你不僅學到技術,更能瞭解軟體開發的實際應用與挑戰。希望透過這個平台,能與你共同成長,激發對技術的熱情!
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
題目敘述 Combination Sum II_Leetcode #40 給定一個整數陣列candidates 和 目標值target。 每個陣列元素只能選擇一次,請問有多少種組合方法,可以使得組合數總和 = target? 請把滿足組合數總和 = target的組合方法以陣列的形式返回答案
Thumbnail
題目敘述 Combination Sum II_Leetcode #40 給定一個整數陣列candidates 和 目標值target。 每個陣列元素只能選擇一次,請問有多少種組合方法,可以使得組合數總和 = target? 請把滿足組合數總和 = target的組合方法以陣列的形式返回答案
Thumbnail
題目敘述 Combination Sum IV 給定一個輸入陣列nums,和目標值target,從nums裡面挑數字去湊出總和 = target,數字可以重複挑選。 請問有多少排列數可以湊出target? 註: 排列數的意思就是位置不同代表兩種不同的方法數。
Thumbnail
題目敘述 Combination Sum IV 給定一個輸入陣列nums,和目標值target,從nums裡面挑數字去湊出總和 = target,數字可以重複挑選。 請問有多少排列數可以湊出target? 註: 排列數的意思就是位置不同代表兩種不同的方法數。
Thumbnail
Leetcode 精選75題 題目與題解 熱門考點 目錄 (持續更新中) 建議從左側目錄 或者 按Ctrl+F輸入關鍵字進行搜尋
Thumbnail
Leetcode 精選75題 題目與題解 熱門考點 目錄 (持續更新中) 建議從左側目錄 或者 按Ctrl+F輸入關鍵字進行搜尋
Thumbnail
知道如何從一組給定的英文字母和單字庫中的單字拼出最高分的單字組合。使用DFS + 回溯法 + 剪枝優化的演算法,詳細分析瞭如何展開所有可能的路徑,並且找出符合條件的狀態,協助讀者理解演算法背後的思維和方法。
Thumbnail
知道如何從一組給定的英文字母和單字庫中的單字拼出最高分的單字組合。使用DFS + 回溯法 + 剪枝優化的演算法,詳細分析瞭如何展開所有可能的路徑,並且找出符合條件的狀態,協助讀者理解演算法背後的思維和方法。
Thumbnail
題目會給定一個陣列nums和一個目標值goal。計算子陣列總和=goal的數目有多少。演算法包含前綴和和字典的技巧,時間複雜度為O(n),空間複雜度為O(n)。
Thumbnail
題目會給定一個陣列nums和一個目標值goal。計算子陣列總和=goal的數目有多少。演算法包含前綴和和字典的技巧,時間複雜度為O(n),空間複雜度為O(n)。
Thumbnail
題目敘述 題目會給定一個輸入字串s和一套編碼規則,要求我們針對字串s進行解碼,並且以字串的形式返回答案。 編碼規則: 數字[字串] -> []內的字串以對應倍數做展開,而且允許巢狀編碼。 例如: 3[a] 解碼完就是 aaa 2[bc] 解碼完就是 bcbc 2[a2[b]] = 2
Thumbnail
題目敘述 題目會給定一個輸入字串s和一套編碼規則,要求我們針對字串s進行解碼,並且以字串的形式返回答案。 編碼規則: 數字[字串] -> []內的字串以對應倍數做展開,而且允許巢狀編碼。 例如: 3[a] 解碼完就是 aaa 2[bc] 解碼完就是 bcbc 2[a2[b]] = 2
Thumbnail
題目敘述 題目會給定我們一個整數陣列nums,我們每回合可以挑選總和為K的兩個數字,形成一個K-Sum pair。 請問我們最多可以製造幾個K-Sum pair? 題目的原文敘述 測試範例 Example 1: Input: nums = [1,2,3,4], k = 5 Output
Thumbnail
題目敘述 題目會給定我們一個整數陣列nums,我們每回合可以挑選總和為K的兩個數字,形成一個K-Sum pair。 請問我們最多可以製造幾個K-Sum pair? 題目的原文敘述 測試範例 Example 1: Input: nums = [1,2,3,4], k = 5 Output
Thumbnail
題目敘述 題目會給兩個陣列nums1和nums2。 題目要求我們從中同步選擇長度為k的子序列,並且最大化子序列的分數, 回傳最高的分數值。 分數的定義: 分數 = (nums1[i0] + nums1[i1] +...+ nums1[ik - 1]) * min(nums2[i0] ,
Thumbnail
題目敘述 題目會給兩個陣列nums1和nums2。 題目要求我們從中同步選擇長度為k的子序列,並且最大化子序列的分數, 回傳最高的分數值。 分數的定義: 分數 = (nums1[i0] + nums1[i1] +...+ nums1[ik - 1]) * min(nums2[i0] ,
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News