题目:数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且有效的括号组合。
原题的LeetCode链接:https://leetcode-cn.com/problems/generate-parentheses/
这道题有递归和动态规划两种思路,本文先讲解递归思路,DP思路过后补充。
有些题解说这可以看成深度优先or广度优先问题,我觉得这样想很牵强。树的构建和遍历包含着递归的思想,而不是递归的思想基于树,所以我还是觉得直接去想递归的思路最好,抓住本质。
我们可以通过递归的方式枚举出所有可能的排列,然后进行判断:
class Solution {
public List<String> generateParenthesis(int n) {
List<String> res = new ArrayList<>();
generateAll(new char[2*n], 0, res);
return res;
}
public void generateAll(char[] cur, int pos, List<String> res) {
if(cur.length == pos) {
if(valid(cur)) res.add(new String(cur));
}
else {
cur[pos]='(';
generateAll(cur,pos+1,res);
cur[pos]=')';
generateAll(cur,pos+1,res);
}
}
public boolean valid(char[] cur) {
int balance=0;
for (char c : cur) {
if(c=='(') balance++;
else{
balance--;
if(balance<0) return false;
}
}
return (balance==0);
}
}
这种方法的时间复杂度为 o ( 2 n ∗ n ) o\left ( 2^{n}*n \right ) o(2n∗n) 通过剪枝操作,可以降低是时间复杂度。
剪枝操作的方法叫做回溯法,在人工智能课程里被列为单独的一个章节,有兴趣的同学可以自己去看。
class Solution {
public List<String> generateParenthesis(int n) {
List<String> res = new ArrayList<>();
generate(new StringBuffer(), 0, 0, n, res);
return res;
}
public void generate(StringBuffer cur, int left, int right, int max, List<String> res) {
if(cur.length() == 2*max) {
res.add(cur.toString());//转换成String类型
}
else {
if(left<max) {
cur.append('(');
generate(cur,left+1,right, max, res);
cur.deleteCharAt(cur.length()-1);
}
if(right<left) {
cur.append(')');
generate(cur,left,right+1, max, res);
cur.deleteCharAt(cur.length()-1);
}
}
}
}
此时时间复杂度大大降低,具体的计算方法是一个卡特兰数,感兴趣的同学可以算算看。
类似题目:LC17.电话号码的字母组合
用到的方法依旧是回溯法
class Solution {
public List<String> output = new ArrayList<>();
public List<String> letterCombinations(String digits) {
if(digits.length()==0) return output;
Map<String,String> map = new HashMap<>();
map.put("2","abc");
map.put("3","def");
map.put("4","ghi");
map.put("5","jkl");
map.put("6","mno");
map.put("7","pqrs");
map.put("8","tuv");
map.put("9","wxyz");
combination(map,digits,new String());
return output;
}
public void combination(Map<String,String> map, String digits, String curr) {
if(digits.length()==0){
output.add(curr);
//String是基本类型,传入的值是内容而不是地址,注意这个和List的区别
return;
}
String s = map.get(digits.substring(0,1));
for(int i=0;i<s.length();i++) combination(map,digits.substring(1),curr + s.charAt(i));
}
}