[LeetCode解題攻略] 22. Generate Parentheses

[LeetCode解題攻略] 22. Generate Parentheses

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

題目描述

給定一個整數 n,請生成所有可能由 n 對括號組成的 有效括號組合


範例 1:
Input: n = 3
Output: ["((()))", "(()())", "(())()", "()(())", "()()()"]

範例 2:
Input: n = 1
Output: ["()"]


解題思路

本題是經典的回溯問題,主要挑戰在於:

  1. 正確地生成所有組合。
  2. 確保生成的括號組合有效。

解法一:回溯法 (Backtracking)

思路

  1. 使用遞歸函數進行回溯:
    • 當前生成的括號序列存儲在變量 current 中。
    • open 表示目前已使用的左括號數量。
    • close 表示目前已使用的右括號數量。
  2. 遞歸條件:
    • 如果左括號的數量小於 n,則可以繼續添加左括號。
    • 如果右括號的數量小於左括號,則可以繼續添加右括號。
  3. 終止條件:
    • 當 current 長度等於 2 * n(即 n 對括號的總字符數),將 current 加入結果集。
  4. 使用回溯法逐步生成所有可能的組合,並通過條件檢查確保有效性。

程式碼實現 (Python)

class Solution:
def generateParenthesis(self, n: int) -> List[str]:
result = []

def backtrack(current: str, open: int, close: int):
# 終止條件
if len(current) == 2 * n:
result.append(current)
return

# 如果可以添加左括號
if open < n:
backtrack(current + "(", open + 1, close)

# 如果可以添加右括號
if close < open:
backtrack(current + ")", open, close + 1)

# 開始回溯
backtrack("", 0, 0)
return result

時間與空間複雜度

  • 時間複雜度:O(4^n / √n)
  • 空間複雜度:O(n)

解法二:動態規劃 (Dynamic Programming)

思路

  1. 使用動態規劃的思想構造結果。
  2. 定義 dp[i] 表示由 i 對括號組成的所有合法括號組合。
  3. 對於每個 i,合法的組合可以表示為:
    • 將 p 對括號組合包裹在一對括號中:("(" + dp[p] + ")" + dp[q]),其中 p + q = i - 1。
  4. 從小到大逐步計算所有組合。

程式碼實現 (Python)

class Solution:
def generateParenthesis(self, n: int) -> List[str]:
dp = [[] for _ in range(n + 1)]
dp[0] = [""] # 0 對括號時,只有空字串

for i in range(1, n + 1):
for p in range(i):
q = i - 1 - p
for left in dp[p]:
for right in dp[q]:
dp[i].append(f"({left}){right}")

return dp[n]

時間與空間複雜度

  • 時間複雜度:O(4^n / √n)
  • 空間複雜度:O(4^n / √n)

解法三:暴力生成 + 有效性檢查

思路

  1. 生成所有可能由 n 對括號組成的字符串組合(例如排列 2n 個括號的所有方式)。
  2. 檢查每個組合是否有效,將有效的加入結果集。
  3. 檢查括號有效性
    • 從左到右遍歷字符串,用變量 balance 表示當前的括號平衡值。
    • 遇到左括號 (balance + 1;遇到右括號 )balance - 1
    • 如果在遍歷過程中 balance < 0,則字符串無效。
    • 最終檢查 balance == 0,確保左右括號數量相等。

程式碼實現 (Python)

class Solution:
def generateParenthesis(self, n: int) -> List[str]:
from itertools import product

def is_valid(sequence: str) -> bool:
balance = 0
for char in sequence:
if char == "(":
balance += 1
else:
balance -= 1
if balance < 0:
return False
return balance == 0

# 生成所有可能的組合
all_combinations = product("()", repeat=2 * n)
result = ["".join(seq) for seq in all_combinations if is_valid(seq)]
return result

時間與空間複雜度

  • 時間複雜度:O(2^(2n) * n),生成所有排列需要 2^(2n),檢查有效性需要 O(n)。
  • 空間複雜度:O(2^(2n)),用於存儲所有可能的組合。

總結

本題是經典的括號生成問題,最常用的方法是 回溯法,它結構清晰且效率高,能夠在有限的遞歸深度內生成所有合法組合。動態規劃法則適合於理解更為結構化的解法,暴力法則可作為基礎練習。

avatar-img
追極光的北極熊|軟體工程師的小天地
6會員
115內容數
歡迎來到我的部落格!這裡記錄了軟體工程師的日常生活點滴,並分享程式設計與演算法的實用教學。無論你是初學者還是有經驗的開發者,都能在這裡找到深入淺出的技術解析與實戰技巧。此外,我也會分享工作中的心路歷程與學習心得,讓你不僅學到技術,更能瞭解軟體開發的實際應用與挑戰。希望透過這個平台,能與你共同成長,激發對技術的熱情!
留言
avatar-img
留言分享你的想法!
給定兩個遞增排序的鏈表 list1 和 list2,將它們合併為一個新的 遞增排序 的鏈表,並返回這個新鏈表。新的鏈表應該由這兩個鏈表中的節點組成,而不應創建新的節點。最後回傳合併後的鍊錶的頭節點。
給定一個僅包含字元 '(', ')', '{', '}', '[' 和 ']' 的字串 s,確定輸入字串是否有效。
給定一個Linked list的head,從這個Linked list的末尾刪除第 n 個節點並返回其head。
給定一個包含 n 個整數的陣列 nums,傳回所有唯一的四元組 [nums[a], nums[b], nums[c], nums[d]] 的數組,使得該答案滿足題目的每項要求。
給定一組字符串,找出它們的最長公共前綴 (Longest Common Prefix)。如果沒有公共前綴,返回空字串 ""。
給定一個包含 2 到 9 之間數字的字串,傳回該數字可以表示的所有可能的字母組合。以任意順序回傳答案。 參考附圖為數字對字母的映射(就像電話按鈕一樣)。
給定兩個遞增排序的鏈表 list1 和 list2,將它們合併為一個新的 遞增排序 的鏈表,並返回這個新鏈表。新的鏈表應該由這兩個鏈表中的節點組成,而不應創建新的節點。最後回傳合併後的鍊錶的頭節點。
給定一個僅包含字元 '(', ')', '{', '}', '[' 和 ']' 的字串 s,確定輸入字串是否有效。
給定一個Linked list的head,從這個Linked list的末尾刪除第 n 個節點並返回其head。
給定一個包含 n 個整數的陣列 nums,傳回所有唯一的四元組 [nums[a], nums[b], nums[c], nums[d]] 的數組,使得該答案滿足題目的每項要求。
給定一組字符串,找出它們的最長公共前綴 (Longest Common Prefix)。如果沒有公共前綴,返回空字串 ""。
給定一個包含 2 到 9 之間數字的字串,傳回該數字可以表示的所有可能的字母組合。以任意順序回傳答案。 參考附圖為數字對字母的映射(就像電話按鈕一樣)。