递归与回溯1:生成全部有效括号组合
从现在开始我们一起来梳理递归相关的问题。递归是高阶算法的核心,回溯、dp都是递归的拓展和延伸。
废话少说,递归的问题一般我们不要急着看代码,因为看了也不懂,而应该先分析问题的本质,从小到大逐步归纳出特征,最后再写代码。而计算机调用的时候恰好是从大小到逐步递进的。
先看一个CC150中的题目,实现一种算法,打印n对括号中的全部有效组合,即左右括号正确配对。
示例:输入n=3,输出:((())), (()()), (())(), ()(()),()()()所以有3个。
这个题如果用递归的话怎么做呢?
如果直接想是不好想的,我们画一个图看一下:
很明显看到, 从n=2开始就是将上面一层的每一个都是在其左边、右边和包起来三种操作,n从1到2就很明显,从n到3就是图中红色标记的地方。后面的每一层都对前面做相同的三个操作。这就是一个从小到大不断递归的过程。
但是这里有个问题,出现重复了怎么办?例如n=2的时候()()和()()是一样的。显然这时候只保留一种就足够了,后序递归操作也只处理一个就行了,那如何去重呢?我们想到集合能保证元素唯一,所以我们可以将每层的元素用集合来保存。代码如下:
class Solution {
public List<String> generateParenthesis(int n) {
List<String> ans = new ArrayList<String>();
backtrack(ans, new StringBuilder(), 0, 0, n);
return ans;
}
/**
* @param ans 当前递归得到的结果
* @param cur 当前的括号串
* @param open 左括号已经使用的个数
* @param close 右括号已经使用的个数
* @param max 序列长度最大值
*/
public void backtrack(List<String> 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);
}
if (close < open) {
cur.append());
backtrack(ans, cur, open, close + 1, max);
cur.deleteCharAt(cur.length() - 1);
}
}
}
这里虽然有个for循环,但是只是简单的拼接字符串,因此难度不算很大的。
上一篇:
92天倒计时,蓝桥杯省赛备赛攻略来啦~
下一篇:
LeetCode第283题——移动零
