递归与回溯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题——移动零