Total Accepted: 57885
Total Submissions: 176392
Difficulty: Medium
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:
"((()))", "(()())", "(())()", "()(())", "()()()"
一.开始打算用动态规划自底向上的算法,也就是第n个的所有括号数,按照左括号开始的数目进行枚举,后面插入需
要补充的括号数,代码,如下,但是忽略了一种情况
分析了下此方法较复杂,放弃。
class Solution {
public:
vector<string> generateParenthesis(int n) {
if(!n){ vector<string> ve; return ve;}
vector<string> * vectors = new vector<string> [n];
for(int ve = 0; ve < n; ve ++)
{
for( int l_num = ve + 1; l_num > 0; l_num --)
{
string str_pre;
for( int il_num = 0; il_num < l_num; il_num ++)
str_pre.append("(");
str_pre.append(")");
int sub_num = ve + 1 - l_num;
if( !sub_num) //有需要补充的子括号数
{
string str = str_pre;
for( int ir_num = 0; ir_num < l_num -1; ir_num ++)
str.append(")");
vectors[ve].push_back(str);
}else
{
//插在内部
for( vector<string>::iterator ite = vectors[sub_num - 1].begin();ite != vectors[sub_num - 1].end(); ite ++)
{
string str = str_pre;
str.append(*ite);
for( int ir_num = 0; ir_num < l_num -1; ir_num ++)
str.append(")");
vectors[ve].push_back(str);
}
//插在外部
for( int wair_num = 1;wair_num <= l_num - 1; wair_num ++ )
{
for( vector<string>::iterator ite = vectors[sub_num - 1].begin();ite != vectors[sub_num - 1].end(); ite ++)
{
string str = str_pre;
for( int ir_num = 0; ir_num < wair_num; ir_num ++)
str.append(")");
str.append(*ite);
for( int ir_num = 0; ir_num < l_num-1-wair_num; ir_num ++)
str.append(")");
vectors[ve].push_back(str);
}
}
}
}
}
//释放多余内存
for(int i = 0; i < n-1; i++)
vectors[i].clear();
return vectors[n-1];
}
};
二.网上看了某人的算法,所谓的
卡特兰数,代码非常简单,就是建一个二叉树,然后深度搜索,要求每次分支,已经画的左括号数必须大于
右括号数,都画完时返回,代码如下:
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> re;
if(!n)
return re;
string str = "";
generate(re,n,n,str);
return re;
}
void generate(vector<string> &re,int left,int right,string str)
{
if( left == 0 && right == 0)
re.push_back(str);
if( left > 0)
generate( re, left - 1, right, str +"(");
if( right > left && right > 0)
generate( re, left, right - 1, str + ")");
return;
}
};
三.
卡特兰数应用例子:(引用网上例子
入栈出栈顺序问题:给定一个长度为n的不重复数组,求所有可能的入栈出栈顺序。该问题解的个数也是卡特兰数,根据上面的思路,我们也
可以写出一个类似的代码:
- void inoutstack(int in,int out,deque<int> &q,stack<int> &s,deque<int> seq,vector<deque<int>> &result)
- {
- if(!in&&!out)
- {
- result.push_back(q);
- return;
- }
- if(in>0)
- {
- s.push(seq.front());
- seq.pop_front();
- inoutstack(in-1,out,q,s,seq,result);
- seq.push_front(s.top());
- s.pop();
- }
- if(out>0&&in<out)
- {
- q.push_back(s.top());
- s.pop();
- inoutstack(in,out-1,q,s,seq,result);
- s.push(q.back());
- q.pop_back();
- }
- }
栈的次数大于或者等于出栈的次数。在生成括号的问题中,我们利用一个string来保存结果,由于打印左括号时不影响打印右括号,所以无需复
杂的状态恢复。在入栈出栈顺序问题中,由于两次递归调用共享同一个栈和队列,所以我们需要手动恢复其内容。在恢复时,队列会从头部删
除和添加,所以我们采用了deque,它可以在头部添加和删除元素。queue只能在头部删除元素,所以没有采用。
中文:卡特兰数
Catalan数是组合数学中一个常出现在各种计数问题中出现的数列。由以比利时的数学家欧仁·查理·
中文:卡特兰数
Catalan数是组合数学中一个常出现在各种计数问题中出现的数列。由以比利时的数学家欧仁·查理·
卡塔兰 (
1814
–
1894
)命名。
原理:
令h( 0 ) = 1 ,h( 1 )= 1 ,catalan数满足递归式:
h(n) = h( 0 ) * h(n - 1 ) + h( 1 ) * h(n - 2 ) +
+
h(n
-
1
)h(
0
) (其中n
>=
2
)
该递推关系的解为:
h(n) = C(2n,n) / (n + 1 ) (n = 1 , 2 , 3 ,
)
另类递归式: h(n)=((4*n-2)/(n+1))*h(n-1);
前几项为 (OEIS中的数列A000108): 1 , 1 , 2 , 5 , 14 , 42 , 132 , 429 , 1430 , 4862 , 16796 ,
原理:
令h( 0 ) = 1 ,h( 1 )= 1 ,catalan数满足递归式:
h(n) = h( 0 ) * h(n - 1 ) + h( 1 ) * h(n - 2 ) +

该递推关系的解为:
h(n) = C(2n,n) / (n + 1 ) (n = 1 , 2 , 3 ,

另类递归式: h(n)=((4*n-2)/(n+1))*h(n-1);
前几项为 (OEIS中的数列A000108): 1 , 1 , 2 , 5 , 14 , 42 , 132 , 429 , 1430 , 4862 , 16796 ,
58786
,
208012
,
742900
,
2674440
,
9694845
,
35357670
,
129644790
,
477638700
,
1767263190
,
6564120420
,
24466267020
,
91482563640
,
343059613650
,
1289904147324
,
4861946401452
, 
应用
我总结了一下,最典型的四类应用:(实质上却都一样,无非是递归等式的应用,就看你能不能

应用
我总结了一下,最典型的四类应用:(实质上却都一样,无非是递归等式的应用,就看你能不能
分解问题写出递归式了)
1 .括号化问题。
矩阵链乘: P = a1×a2×a3×……×an,依据乘法结合律,不改变其顺序,只用括号表示成对的
1 .括号化问题。
矩阵链乘: P = a1×a2×a3×……×an,依据乘法结合律,不改变其顺序,只用括号表示成对的
乘积,试问有几种括号化的方案?(h(n)种)
2 .出栈次序问题。
一个栈(无穷大)的进栈序列为1, 2 , 3 ,..n,有多少个不同的出栈序列 ?
类似:
( 1 )有2n个人排成一行进入剧场。入场费5元。其中只有n个人有一张5元钞票,另外n人只有
2 .出栈次序问题。
一个栈(无穷大)的进栈序列为1, 2 , 3 ,..n,有多少个不同的出栈序列 ?
类似:
( 1 )有2n个人排成一行进入剧场。入场费5元。其中只有n个人有一张5元钞票,另外n人只有
10元钞票,剧院无其它钞票,问有多少中方法使得只要有10元的人买票,售票处就有5元的钞票
找零?(将持5元者到达视作将5元入栈,持10元者到达视作使栈中某5元出栈)
( 2 )在圆上选择2n个点,将这些点成对连接起来,使得所得到的n条线段不相交的方法数。
3 .将多边行划分为三角形问题。
将一个凸多边形区域分成三角形区域的方法数 ?
类似:一位大城市的律师在她住所以北n个街区和以东n个街区处工作。每天她走2n个街区去上班
( 2 )在圆上选择2n个点,将这些点成对连接起来,使得所得到的n条线段不相交的方法数。
3 .将多边行划分为三角形问题。
将一个凸多边形区域分成三角形区域的方法数 ?
类似:一位大城市的律师在她住所以北n个街区和以东n个街区处工作。每天她走2n个街区去上班
。如果她
从不穿越(但可以碰到)从家到办公室的对角线,那么有多少条可能的道路?
类似:在圆上选择2n个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数 ?
4 .给顶节点组成二叉树的问题。
给定N个节点,能构成多少种形状不同的二叉树?
(一定是二叉树 !
先去一个点作为顶点,然后左边依次可以取0至N - 1个相对应的,右边是N - 1到0个,两两配对相乘,
从不穿越(但可以碰到)从家到办公室的对角线,那么有多少条可能的道路?
类似:在圆上选择2n个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数 ?
4 .给顶节点组成二叉树的问题。
给定N个节点,能构成多少种形状不同的二叉树?
(一定是二叉树 !
先去一个点作为顶点,然后左边依次可以取0至N - 1个相对应的,右边是N - 1到0个,两两配对相乘,
就是h(
0
)
*
h(n
-
1
)
+
h(
2
)
*
h(n
-
2
)
+
+
h(n
-
1
)h(
0
)
=
h(n))
(能构成h(N)个)

(能构成h(N)个)