0
点赞
收藏
分享

微信扫一扫

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;
}
};

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

拉跨(;′⌒`)

第二搏

第一搏中虽然尽可能的进行剪枝,但是每次利用函数对字符串进行合法判断,性能确实影响较大。

考虑到能够利用【左括号数目限制配对】,以及【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 + ")");
}
};

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

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

 

举报

相关推荐

0 条评论