1. 程式人生 > >不積跬步無以至千里——LeetCode 22.括號生成

不積跬步無以至千里——LeetCode 22.括號生成

給出 n 代表生成括號的對數,請你寫出一個函式,使其能夠生成所有可能的並且有效的括號組合。

例如,給出 = 3,生成結果為:

[
  "((()))",
  "(()())",
  "(())()",
  "()(())",
  "()()()"
]

 解題思路:

  1、利用遞迴,傳入儲存結果的列表,傳入一個空字串,和括號的對數,後來發現括號對數要分成左右個數。

  2、先新增 "(" 一種括號,在 n=0 時,新增 ")" ,再去掉一個 "(" ,新增 ")"再添"("  (反正就是先新增左括號,在不能新增時再新增右括號,結果加入list,返回到遞迴的去掉n=1,2,3...n個左括號的狀態)

  3、後面查閱資料發現這是一種演算法:回溯演算法,後面做下具體記錄

我的記錄:

class Solution {
    public List<String> generateParenthesis(int n) {
        ArrayList<String> result = new ArrayList<String>();
        parenthes("", result, n, n);
        return result;
    }
    
    public void parenthes(String sublist, ArrayList result,int
leftnum, int rightnum){ if(leftnum == 0 && rightnum == 0){ result.add(sublist); } if(leftnum > 0){ parenthes(sublist + "(", result, leftnum - 1, rightnum); } if(rightnum > leftnum){ parenthes(sublist + ")", result, leftnum, rightnum - 1); } } }

LeetCode官方題解之回溯法:

只有在我們知道序列仍然保持有效時才新增 '(' or ')',而不是像方法一那樣每次新增。我們可以通過跟蹤到目前為止放置的左括號和右括號的數目來做到這一點,

如果我們還剩一個位置,我們可以開始放一個左括號。 如果它不超過左括號的數量,我們可以放一個右括號。

class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> ans = new ArrayList();
        backtrack(ans, "", 0, 0, n);
        return ans;
    }

    public void backtrack(List<String> ans, String cur, int open, int close, int max){
        if (cur.length() == max * 2) {
            ans.add(cur);
            return;
        }

        if (open < max)
            backtrack(ans, cur+"(", open+1, close, max);
        if (close < open)
            backtrack(ans, cur+")", open, close+1, max);
    }
}

複雜度分析

我們的複雜度分析依賴於理解 generateParenthesis(n) 中有多少個元素。這個分析超出了本文的範疇,但事實證明這是第 n 個卡塔蘭數 \dfrac{1}{n+1}\binom{2n}{n}n+11(n2n),這是由 \dfrac{4^n}{n\sqrt{n}}nn4n 漸近界定的。

  • 時間複雜度:O(\dfrac{4^n}{\sqrt{n}})O(n4n),在回溯過程中,每個有效序列最多需要 n 步。

  • 空間複雜度:O(\dfrac{4^n}{\sqrt{n}})O(n4n),如上所述,並使用 O(n)O(n) 的空間來儲存序列。 

LeetCode官方題解之閉合數法:

思路

為了列舉某些內容,我們通常希望將其表示為更容易計算的不相交子集的總和。

考慮有效括號序列 S 的閉包數:至少存在index> = 0,使得 S[0], S[1], ..., S[2*index+1]是有效的。 顯然,每個括號序列都有一個唯一的閉包號。 我們可以嘗試單獨列舉它們。

演算法

對於每個閉合數 c,我們知道起始和結束括號必定位於索引 0 和 2*c + 1。然後兩者間的 2*c 個元素一定是有效序列,其餘元素一定是有效序列。

class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> ans = new ArrayList();
        if (n == 0) {
            ans.add("");
        } else {
            for (int c = 0; c < n; ++c)
                for (String left: generateParenthesis(c))
                    for (String right: generateParenthesis(n-1-c))
                        ans.add("(" + left + ")" + right);
        }
        return ans;
    }
}

複雜度分析

  • 時間和空間複雜度:O(\dfrac{4^n}{\sqrt{n}})O(n4n),該分析與回溯法類似。