1. 程式人生 > 其它 >LeetCode_String_22. Generate Parentheses 括號生成(C++/Java)【DFS、剪枝、括號匹配】

LeetCode_String_22. Generate Parentheses 括號生成(C++/Java)【DFS、剪枝、括號匹配】

技術標籤:LeetCodeLeetCodeDFS回溯剪枝括號匹配中等難度String

目錄

一,題目描述

英文描述

中文描述

二,解題思路

三,AC程式碼

C++

Java

四,解題過程

第一博

第二搏


一,題目描述

原題連結https://leetcode-cn.com/problems/generate-parentheses/

英文描述

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

Example 1:

Input: n = 3

Output: ["((()))","(()())","(())()","()(())","()()()"]


Example 2:

Input: n = 1
Output: ["()"]

Constraints:

1 <= n <= 8

中文描述

數字 n代表生成括號的對數,請你設計一個函式,用於能夠生成所有可能的並且 有效的 括號組合。

示例:

輸入:n = 3
輸出:[
"((()))",
"(()())",

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

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/generate-parentheses
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

二,解題思路

在DFS尋找所有可能結果的同時進行【剪枝】、【判斷是否符合條件】。

  • n表示完美匹配的括號數目,而且必須先有左括號,才能出現右括號;
  • 利用numOfLeft記錄左括號數目,也就是說numOfLeft滿足【numOfLeft<0||numOfLeft>n】,超出這個範圍的字串,就一定不能完美匹配,因此可以通過剪枝pass掉;
  • 當字串長度順利達到2n時,只需要判斷左括號數目是否達到n,即可判定字串是否滿足條件;

於是,優雅的題解程式碼便誕生了o(* ̄▽ ̄*)ブ

三,AC程式碼

C++

class Solution {
public:
    vector<string> ans;
    vector<string> generateParenthesis(int n) {
        dfs(n, 0, "");
        return ans;
    }
    void dfs(int n, int numOfLeft, string tem) {
        // 字串長度達到2n標準
        if(tem.size() >= 2 * n) {
            // 左括號與右括號完美匹配
            if(numOfLeft == 0) ans.push_back(tem);
            return;
        }
        // 左括號數目超出能夠匹配的範圍
        if(numOfLeft < 0 || numOfLeft > n) return;
        // 左括號數目加一
        dfs(n, numOfLeft + 1, tem + "(");
        // 左右括號抵消,左括號數目減一
        dfs(n, numOfLeft - 1, tem + ")");
    }
};

Java

由於Java處理字串比較麻煩,程式碼細節部分處理與C++有較大不同

class Solution {
    List<String> ans = new ArrayList<String>();
    public List<String> generateParenthesis(int n) {
        // java中字串是常量,不能直接改動,需要藉助StringBuilder進行字串操作
        dfs(n, 0, new StringBuilder());
        return ans;
    }
    public void dfs(int n, int numOfLeft, StringBuilder tem) {
        // 字串長度達到2n標準
        if(tem.length() >= 2 * n) {
            // 左括號與右括號完美匹配
            try {
                // 不使用try/catch,這裡直接判斷會報空指標異常  
                if(numOfLeft == 0) ans.add(tem.toString());
            } catch(Exception e) {
            } finally {
                return;
            }
        }
        // 左括號數目超出能夠匹配的範圍
        if(numOfLeft < 0 || numOfLeft > n) return;
        // 左括號數目加一
        tem.append("(");                    // 入棧
        dfs(n, numOfLeft + 1, tem);
        tem.deleteCharAt(tem.length() - 1); // 出棧
        // 左右括號抵消,左括號數目減一
        tem.append(")");                    // 入棧
        dfs(n, numOfLeft - 1, tem);
        tem.deleteCharAt(tem.length() - 1); // 入棧
    }
}

四,解題過程

第一博

利用DFS獲取並判斷所有可能的解。

  • 利用DFS遍歷所有可能的括號組合,並根據左括號數目的限制(不能小於0或大於n)進行剪枝;
  • DFS過程中,一旦字串長度達到標準2n,進入判定函式判斷括號是否完美匹配;
  • 若完美匹配,則將其存入結果集中;
class Solution {
public:
    vector<string> ans;
    vector<string> generateParenthesis(int n) {
        dfs(n, 0, "");
        return ans;
    }
    void dfs(int n, int numOfLeft, string tem) {
        if(tem.size() >= 2 * n) {
            if(isLegal(tem)) {
                ans.push_back(tem);
            }
            return;
        }
        if(numOfLeft < 0 || numOfLeft > n) return;
        dfs(n, numOfLeft + 1, tem + "(");
        dfs(n, numOfLeft - 1, tem + ")");
    }
    bool isLegal(string s) {
        int leftBracketNum = 0;
        for(int i = 0; i < s.size(); i++) {
            if(s.substr(i, 1) == "(") leftBracketNum++;
            else {
                if(leftBracketNum == 0) return false;
                leftBracketNum--;
            }
        }
        return leftBracketNum == 0 ?  true : false;
    }
};

拉跨(;′⌒`)

第二搏

第一搏中雖然儘可能的進行剪枝,但是每次利用函式對字串進行合法判斷,效能確實影響較大。

考慮到能夠利用【左括號數目限制配對】,以及【DFS過程中優先選擇左括號入棧】,是否可以省去判斷字串是否滿足條件的步驟?

當然是可以的。

由於左括號入棧(把字元新增到tem末尾當作入棧),numOfLeft加一,右括號入棧,numOfLeft減一。如果完美匹配,那麼在字串長度達到2n時,numOfLeft必定為0

class Solution {
public:
    vector<string> ans;
    vector<string> generateParenthesis(int n) {
        dfs(n, 0, "");
        return ans;
    }
    void dfs(int n, int numOfLeft, string tem) {
        // 字串長度達到2n標準
        if(tem.size() >= 2 * n) {
            // 左括號與右括號完美匹配
            if(numOfLeft == 0) ans.push_back(tem);
            return;
        }
        // 左括號數目超出能夠匹配的範圍
        if(numOfLeft < 0 || numOfLeft > n) return;
        // 左括號數目加一
        dfs(n, numOfLeft + 1, tem + "(");
        // 左右括號抵消,左括號數目減一
        dfs(n, numOfLeft - 1, tem + ")");
    }
};

可以看出時間和空間確實得到了優化*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。