【题目描述】
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 【题目链接】. - 力扣(LeetCode)
【解题代码】
java
package dp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class GenerateParenthesis {
// 生成的有效括号字符串列表
private List<String> result;
// StringBuilder变量,用于生成有效括号字符串
private StringBuilder sb;
// 左括号数量
private int count1;
// 右括号数量
private int count2;
public static void main(String[] args) {
long start = System.currentTimeMillis();
List<String> result = new GenerateParenthesis().generateParenthesis(3);
System.out.println("result = " + Arrays.toString(result.toArray()));
System.out.println("函数执行时间:" + (System.currentTimeMillis() - start) + "MS");
}
public List<String> generateParenthesis(int n) {
// 初始化各个变量
result = new ArrayList<>();
sb = new StringBuilder();
count1 = 0;
count2 = 0;
// 有效的括号字符串肯定,第一个肯定是左括号
generateParenthesis(n, 0);
// 返回最终结果
return result;
}
/**
* 生成有效括号字符串
* @param n 有效括号组数量
* type:当前添加括号的类型,0:左括号,1:右括号
**/
private void generateParenthesis(int n, int type) {
// 当前添加左括号
if (type == 0) {
sb.append('(');
count1++;
} else { // 当前添加右括号
sb.append(')');
count2++;
// 右括号数量等于n,说明新的有效括号组完成,添加到结果类表中
if (count2 == n) {
result.add(sb.toString());
return;
}
}
// 先处理左括号,如果左括号数小于n,添加左括号
if (count1 < n) {
generateParenthesis(n, 0);
// 回溯,删除这一层递归函数里添加的左括号,并将左括号数减一
sb.deleteCharAt(sb.length() - 1);
count1--;
}
// 再处理右括号,右括号只能数量小于左括号时才能添加
if (count2 < count1) {
generateParenthesis(n, 1);
// 回溯,删除这一层递归函数里添加的左括号,并将左括号数减一
sb.deleteCharAt(sb.length() - 1);
count2--;
}
}
}
【解题思路】
分析题目,仔细思考得到以下几个思路点:
- 所谓有效括号就是最终的左括号数和右括号数一样;
- 右括号出现时,前面至少有一个左括号能与之匹配,也就是右括号的数量必须小于等于已有的左括号数。
- 此道题目可以按照**"回溯递归"**的方式进行处理 ,即当前一步添加左括号->然后递归处理下一步->递归回到这一层->然后弹出左括号->然后添加右括号的->然后递归处理下一步。
- 第一步肯定要添加左括号
【解题步骤】
-
首先要给解题类添加几个成员变量:包括最终的结果字符串列表、存储过程字符串的StringBuilder、左右括号的数量等,并在构造函数中进行初始化
java// 生成的有效括号字符串列表 private List<String> result; // StringBuilder变量,用于生成有效括号字符串 private StringBuilder sb; // 左括号数量 private int count1; // 右括号数量 private int count2; public GenerateParenthesis(){ // 初始化各个变量 result = new ArrayList<>(); sb = new StringBuilder(); count1 = 0; count2 = 0; }
-
定义一个**"回溯递归"生成有效括号字符串的函数generateParenthesis** ,传入参数包括有效括号组数量,当前添加括号类型
java/** * 生成有效括号字符串 * @param n 有效括号组数量 * type:当前添加括号的类型,0:左括号,1:右括号 **/ private void generateParenthesis(int n, int type)
-
generateParenthesis 函数内部第一步:根据当前要添加括号类型,存储对应的括号字符串并进行计数处理。如果是右括号则判断当前括号组数是否等于n,如果等于则将当前字符串加入结果列表中
java// 当前添加左括号 if (type == 0) { sb.append('('); count1++; } else { // 当前添加右括号 sb.append(')'); count2++; // 右括号数量等于n,说明新的有效括号组完成,添加到结果类表中 if (count2 == n) { result.add(sb.toString()); return; } }
-
generateParenthesis 函数内部第二步:下一步递归添加左括号,递归完毕后进行回溯
java// 先处理左括号,如果左括号数小于n,添加左括号 if (count1 < n) { generateParenthesis(n, 0); // 回溯,删除这一层递归函数里添加的左括号,并将左括号数减一 sb.deleteCharAt(sb.length() - 1); count1--; }
-
generateParenthesis 函数内部第二步:下一步递归添加右括号,递归完毕后进行回溯,只有在右括号数量小于左括号的情况下才能添加右括号
java// 再处理右括号,右括号只能数量小于左括号时才能添加 if (count2 < count1) { generateParenthesis(n, 1); // 回溯,删除这一层递归函数里添加的左括号,并将左括号数减一 sb.deleteCharAt(sb.length() - 1); count2--; }
-
最后主函数generateParenthesis里调用递归函数,从第一个左括号添加开始,最后返回结果列表即可
javapublic List<String> generateParenthesis(int n) { // 有效的括号字符串肯定,第一个肯定是左括号 generateParenthesis(n, 0); // 返回最终结果 return result; }
【思考总结】
- 掌握"回溯":回溯算法也叫试探法,它是一种系统地搜索问题的解的方法。回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试;
- 大的算法框架定下来后, 要细心找出业务里面的逻辑关键点,这道理题的关键点就在于"只有在右括号数量小于左括号的情况下才能添加右括号"
- 算法优化里,要掌握所运用语言库的性能最优方式,比如这里字符串处理使用了Java库里面的StringBuilder
- LeetCode解题之前,一定不要看题解,看了就"破功"了!