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

目录

一,题目描述

英文描述

中文描述

二,解题思路

三,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 + ")");
    }
};

可以看出时间和空间确实得到了优化*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。

猜你喜欢

转载自blog.csdn.net/qq_41528502/article/details/112794523
今日推荐