目录
题目
解题思路
暴力法
暴力法代码
回溯法
回溯法代码
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例:
输入:n = 3
输出:[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/generate-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思想:将所有可能性列举出来,然后逐个检查每个可能性是否有效。针对这个问题的具体就是:输入n(n对括号),可以生成2^(2n)个由 “ ( ” 和 " ) " 字符构成的序列,然后逐个检查是否有效;
1)生成序列实现方式:
因为长度为n的序列就是在长度为n-1序列的基础上再加一个 “ ( ” 或者 " ) ",所以考虑使用递归。递归解决结构相同的子问题。
2)检查序列是否有效方式:
遍历这个序列,使用变量 balance 表示左括号的数量减去右括号的数量。如果遍历过程中 balance 的值小于0,或者结束时候 balance 的值不为0,那么该序列就是无效的,否则他就是有效的。
1、生成序列递归函数:
2、有效性检查函数:
import java.util.ArrayList;
import java.util.List;
/**
* 括号生成问题
* @author tyler
*
*/
public class Solution{
public List generateParenthesis(int n) {
List combinations = new ArrayList<>();
generateALL(new char[2*n], 0, combinations);
return combinations;
}
// 递归生成序列
public void generateALL(char[] current, int pos, List result) {
// 递归头
if (pos == current.length) {
if (valid(current)) {
// 当前序列有效则将其加入结果集合中
result.add(new String(current));
}
}else {
// 递归体,在子问题的基础上,做相同的操作
current[pos] = '(';
generateALL(current, pos+1, result);
current[pos] = ')';
generateALL(current, pos+1, result);
}
}
// 检查有效性
public boolean valid(char[] current) {
int balance = 0;
for (char c : current) {
if (c == '(') {
balance++;
}else {
balance--;
}
if (balance < 0) {
return false;
}
}
return (balance == 0);
}
}
复杂度分析:
时间复杂度:O(2^{2n}n),对于 2^{2n}个序列中的每一个,我们用于建立和验证该序列的复杂度为 O(n)。
空间复杂度:O(n),除了答案数组之外,我们所需要的空间取决于递归栈的深度,每一层递归函数需要 O(1) 的空间,最多递归 2n 层,因此空间复杂度为 O(n)。
思想:在暴力法的基础上,增加生成子问题的条件,减少列举的可能性数目。
在这儿的具体是:暴力法还有改进的余地:我们可以只在序列仍然保持有效时才添加 '(' or ')',而不是像 方法一 那样每次添加(增加添加序列的条件,减少列举的可能性数目)。我们可以通过跟踪到目前为止放置的左括号和右括号的数目来做到这一点。
如果左括号数量不大于 n,我们可以放一个左括号。如果右括号数量小于左括号的数量,我们可以放一个右括号。
回溯法思想:参考之前的文章:https://blog.csdn.net/Longtermevolution/article/details/105395558
当前位置有两种选择,为了把两种选择都进行探索,所以在探索了当前位置加入 '(' 的解空间之后,需要reset当前位置,再去探索当前位置加入 ')' 的解空间。
import java.util.ArrayList;
import java.util.List;
/**
* 括号生成问题,回溯法
* @author tyler
*
*/
public class Solution{
public List generateParenthesis(int n) {
List ans = new ArrayList();
backtrack(ans, new StringBuilder(), 0, 0, n);
return ans;
}
public void backtrack(List ans, StringBuilder cur, int open, int close, int max){
if (cur.length() == max * 2) {
ans.add(cur.toString());
return;
}
if (open < max) {
cur.append('(');
backtrack(ans, cur, open+1, close, max); // 探索当前位置加入左括号的解空间
cur.deleteCharAt(cur.length() - 1);// 但是当前位置有可能加入右括号,所以需要reset当前位置。
}
if (close < open) {
cur.append(')');
backtrack(ans, cur, open, close+1, max);// 同理
cur.deleteCharAt(cur.length() - 1);
}
}
}