Generate Parentheses
Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
For example, given n = 3, a solution set is:
"((()))", "(()())", "(())()", "()(())", "()()()"
原创地址:http://blog.csdn.net/kenden23/article/details/16951299
此题给人一看就是眼花缭乱的感觉。在纷繁的特征中总结出来规律还真是非常难的。
网上分析实在太少了,令人难以看明白。
所以我在这里分析一下,觉得我分析得不好,或者深度不够的,请多多指教。
以我的思维,我是使用二叉树的思维思考这个算法的,不过最后没能自己成功的写出程序。查了查LeetCode论坛,使用的是递归算法,感觉真是精妙绝伦!因为我考虑这个递归树的时候有两个难点:
1 如何两边同时遍历一棵树?
2 如何剔除不适合的路径?比如最中间的路径是不满足条件的。
到底是什么样的树?构造出这样的概念树,我觉得就好理解很多了。
看看下面我画的树:
顺着路径对称地,比如最左边和最右边,遍历两个路径,就是一个解,除了最中间的不符合要求。所以要遍历这样的概念树,很困难。
下面是程序出处:http://discuss.leetcode.com/questions/203/generate-parentheses
vector<string> generateParenthesis(int n) {
vector<string> ans;
if (n>0) generator(ans, "", 0, 0, n);
return ans;
}
void generator(vector<string> & ans, string s, int l, int r, int n) { // r/l: appearance of ) (
if (l == n) {
ans.push_back(s.append(n-r, ')'));
return;
}
generator(ans, s+'(', l+1, r, n);
if (l>r) generator(ans, s+")", l, r+1, n);
}
逐句看看吧,反正没多少。
if (l == n) {
ans.push_back(s.append(n-r, ')'));
return;
}
这个功能是已经遍历了左边的树,然后这里并不需要遍历右边的树,而是非常巧妙地直接补上右括号“)”就完事了。没做过的话,真是想破头脑都难想出来。
而这样居然神奇地完成一个解了。
generator(ans, s+'(', l+1, r, n);
这句就是遍历左递归树,是先序遍历哦,有人说理解为后序,不对,应该是先序。因为s+'('就是先把"("加进去了,然后到下一层树节点。
左子树递归遍历完了就开始右子树递归遍历:
if (l>r) generator(ans, s+")", l, r+1, n);
右子树都是“)”右括号的,所以s+")"代表遍历右子树。
不过令人头痛的就是怎么理解if(l>r)这个条件?
看了些博客说理解为不能让右括号多于左括号,有一定道理,那为什么前面遍历左子树的时候,就是填写左括号“(”的时候,不添加一个判断左子树不能大于右子树的条件呢?所以这样理解好像不大合理。
我的理解,这句正是剔除中间不符合条件的路径的巧妙句子。
因为只有左子树的最右边的子树才会可能出现右括号")"大于左括号"("的情况的,所以这个条件真是"黄娟! 幼妇! 外孙"!
2014-1-25 update
递归回溯法的程序,其实题目并不难,上面的分析我重新看看,使用树的思维分析也可以,不过如果熟悉了,那么还是不需要分析的太麻烦,不过这样分析对思维锻炼很有好处。
本题最难的就是一个条件判断啦,如我重新写了个程序,如下。这个条件就是if (left < right),这是个十分难想出来的条件,最后怎么吃透这个条件呢?
我就是使用实例,反复走几遍,然后就确立了这个条件,这就是根据实例观察其中的规律的能力了。
虽然就是那么一个条件,自己摸索出来,难度还是相当大的。所以本题我觉得因人而异才能确定其难度指数,我觉得相对难度应该达到4.5星级了,不注意的话,面试的时候是很难写出正确的程序的。也许写个差不多的程序是可以的,不过所谓的差不多程序,就是差一点点没正确的程序,说到底就是错误的程序。
class Solution {
public:
vector<string> generateParenthesis(int n)
{
vector<string> rs;
string s;
genParenthesis(rs, s, n, n);
return rs;
}
void genParenthesis(vector<string> &rs, string &s, int left, int right)
{
if (left==0)
{
rs.push_back(s);
rs.back().append(right, ')');
return;
}
s.push_back('(');
genParenthesis(rs, s, left-1, right);
s.pop_back();
if (left < right)
{
s.push_back(')');
genParenthesis(rs, s, left, right-1);
s.pop_back();
}
}
};
难度指数: 4.5星级