我的LeetCode代码仓:https://github.com/617076674/LeetCode
原题链接:https://leetcode-cn.com/problems/generate-parentheses/description/
题目描述:
知识点:递归、回溯
思路一:暴力解法(没有剪枝操作的回溯)
先生成出所有可能的括号组合,再筛选出有效的括号组合。
对于n对括号,其生成的括号组合字符串的长度为2n,因此总共可以生成(2 ^ n)个字符串,在其中筛选出有效的字符串。
由于题目所给的有效括号中只有小括号,我们不需要采用LeetCode020——有效括号的方法,用一个栈来判断,我们只需要遍历字符串,在字符串的每一个字符位置处记录左括号的个数和右括号的个数,如果任一字符的位置处出现了右括号的个数大于左括号的个数,那么这个字符串就是无效的括号组合。最后遍历完该字符串后我们的左括号个数应该和右括号个数相等。
通过上述分析,此思路的时间复杂度是(n * 2 ^ n)级别的。我们将每一个生成的字符串都视为是有效的,而总共有2 ^ n个字符串,每个字符串的长度为n,因此空间复杂度是O(n * 2 ^ n)级别的。
JAVA代码:
public class Solution {
private List<String> list;
List<Character> brackets;
public List<String> generateParenthesis(int n) {
list = new ArrayList<>();
brackets = new ArrayList<>();
brackets.add('(');
brackets.add(')');
generateParenthesis(new StringBuilder(), 0, n);
return list;
}
//stringBuilder中存储了索引为0 ~ (index - 1)的字符,现在考虑第index索引位置的字符
private void generateParenthesis(StringBuilder stringBuilder, int index, int n) {
if(index == 2 * n) {
if(isValidString(stringBuilder.toString())) {
list.add(stringBuilder.toString());
}
return;
}
for(int i = 0; i < brackets.size(); i++) {
stringBuilder.append(brackets.get(i));
generateParenthesis(stringBuilder, index + 1, n);
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
}
}
private boolean isValidString(String string) {
int leftCount = 0;
int rightCount = 0;
for(int i = 0; i < string.length(); i++) {
if(string.charAt(i) == '(') {
leftCount++;
}else {
rightCount++;
}
if(rightCount > leftCount) {
return false;
}
}
return rightCount == leftCount;
}
}
LeetCode解题报告:
思路二:在思路一的基础上通过剪枝操作,提前消去无效的字符串
因为要穷举出所有可能的并且有效的括号组合,本题的回溯解法还是很容易想到的。
在递归的过程中,只有在我们知道序列仍然保持有效时才添加 '(' or ')' ,而不是像思路一那样每次添加。我们可以通过设置两个变量leftCount和rightCount来分别跟踪到目前为止放置的左括号和右括号的数目来做到这一点。
回溯过程中注意以下几点。
(1)在Solution类中新建内部的私有成员变量,可以防止递归函数中一直传递该变量。
(2)注意相关变量的手动回溯。
这个思路的时间复杂度和空间复杂度分析很复杂,超出了我们所能推导的范围,这里做一个了解。
时间复杂度为O(),在回溯过程中,每个有效序列最多需要n步。空间复杂度即递归深度也为O(),并使用O(n)的空间来存储有效序列。
JAVA代码:
public class Solution {
List<String> list;
List<Character> brackets;
public List<String> generateParenthesis(int n) {
list = new ArrayList<>();
brackets = new ArrayList<>();
brackets.add('(');
brackets.add(')');
generateParenthesis(new StringBuilder(), 0, 0, n);
return list;
}
//stringBuilder中存放了leftCount个左括号,rightCount个右括号
private void generateParenthesis(StringBuilder stringBuilder, int leftCount, int rightCount, int n) {
if(leftCount + rightCount == 2 * n) {
list.add(stringBuilder.toString());
return;
}
if(leftCount == rightCount) {
stringBuilder.append(brackets.get(0));
generateParenthesis(stringBuilder, leftCount + 1, rightCount, n);
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
}else if(leftCount > rightCount) {
if(leftCount == n) {
stringBuilder.append(brackets.get(1));
generateParenthesis(stringBuilder, leftCount, rightCount + 1, n);
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
}else if(leftCount < n) {
for(int i = 0; i < brackets.size(); i++) {
stringBuilder.append(brackets.get(i));
if(i == 0) {
generateParenthesis(stringBuilder, leftCount + 1, rightCount, n);
}else {
generateParenthesis(stringBuilder, leftCount, rightCount + 1, n);
}
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
}
}
}
}
}
LeetCode解题报告:
思路三:闭合数
这个思路参考自LeetCode官网,链接如下:https://leetcode-cn.com/articles/generate-parentheses/#_3。
核心思想:对于一个有效的括号组合,对于其中一对括号而言,这一对括号中间的内容一定是有效的括号组合,而这一对括号外的内容也一定是有效的括号组合。
为了防止出现重复的括号组合,我们假设这一对括号的左边没有内容。因此就变成了这一对括号中间的内容一定是有效的括号组合,而这一对括号右边的内容也一定是有效的括号组合。(我们完全也可以假设这一对括号的左边没有内容,结果是一样的。)
时间复杂度和空间复杂度和思路二一样,均为O(),分析也比较复杂。
JAVA代码:
public class Solution {
public List<String> generateParenthesis(int n) {
List<String> list = new ArrayList<>();
if(n == 0) {
list.add("");
}
for(int i = 0; i < n; i++) {
List<String> list1 = generateParenthesis(i);
List<String> list2 = generateParenthesis(n - i - 1);
for (String s1 : list1) {
for (String s2 : list2) {
list.add("(" + s1 + ")" + s2);
}
}
}
return list;
}
}
LeetCode解题报告: