me 一个童鞋跟 me 提过一个问题:说1-12 这 12 个数,分成 2 组,然后每组按大小排序,其中一组中的数总是比另外一组中对应顺序的数要大,问有多少种情况?me 还真做不出来,他告诉 me 说这是Catalan数。即使他这么说,me 貌似还是不太明白。不过这不影响,me 简单搜索一下这个数(其实me以前就有所耳闻),有好几个用处,简单罗列一下。
平衡括号
平衡括号:在一个合法的算术表达式中可以出现的括号都是平衡括号,更专业一点的说法应该是“所有的左括号和右括号可以建立一对一的对应关系,并且左括号在对应的右括号左侧”。像()(), (())(), (()())(()) 都是平衡括号,但是(()))(, ()())() 就不是平衡括号。
观察一下特点,判断所给的一个括号串是不是平衡很容易:设置一个计数器,初始值为 0,然后从左向右数,遇到左括号 +1,遇到右括号 -1,在过程中不出现负数,并且最终的计数器还是 0,那么就是平衡括号;除此之外就是非平衡括号。
现在的问题是,如果有 n 对括号,问平衡的情况有多少种?暂时可以数一下 n = 1, 2, 3, 4 时的情况:
n = 1: () n = 2: ()(), (()) n = 3: ()()(), ()(()), (())(), (()()), ((())) n = 4:()()()(), ()()(()), ()(())(), ()(()()), ()((())), (())()(), (())(()), (()())(), ((()))(), (()()()), (()(())), ((())()), ((()())), (((())))
入栈出栈
问:n 个数入栈出栈,问情况有多少种?
比如全部入进去然后倒退出栈是 1 种;入一个出一个,全部都这样,是 1 种。入栈出栈的情况和n个数相不相同没有关系。因为可以视第一个入栈的为 1,第二个为 2,最后一个为 n。如果是 n 个相同的数的入栈出栈情况,假设数字都是括号,入栈是左括号,出栈是右括号,那么一种入栈出栈情况应该就对应平衡括号中的一种情况,所以,该问题和问题1是等价问题。
走方格
问:一个 n*n 的方格,从左下角走到右上角,只能向右和向上走,但是不能绕过左下角和右上角确定的对角线,问有多少种走法?
如果将向右走看做是入一下栈,向上走是出一下栈,不能绕道对角线以上,也就是一种合法的入栈出栈,从左下角开始到右上角,要入 n 次栈出 n 次栈,所以一种走法对应一种 n 个数的入栈出栈情况,于是,此问题和问题2是等价问题;
二叉树的形状
问:n 个结点的二叉树的形状有多少种?
所谓的二叉树,就是有一个根结点,有棵左子树还有一棵右子树。n = 3: ()()(), ()(()), (())(), (()()), ((())) 时的括号匹配,me 们换个角度看这个表示:第一个成对的括号是树根,括号内部的子串和括号右边的子串依然是平衡括号串,把内部的看做是左子树,把右边的看成是右子树,比如(())()对应的是有一个左节点和一个右节点的二叉树,()()() 表示只有右子树的二叉树(右子树只有一个右结点);如果上面的对应成立(递归证明),那么二叉树的情况就和问题1 是等价问题;
矩阵链乘
问:A = A0 * A1 * A2 * ... * An,Ai 都是矩阵,式子合法,那么问,如果使用括号来改变计算顺序的话,计算方法有多少种?
本来的乘法运算是从左到右运算,me 们将运算使用栈模拟一下:首先将 A0 压入栈底,然后看 A1,和栈顶的 A0 计算结果,栈顶 A0 出栈而结果入栈;看A2,和栈顶元素计算结果,栈顶元素出栈、结果入栈;...如果使用括号改变了顺序,比如A0*(A1*A2)*...看A1的时候,入栈不计算结果,看A2的时候,计算结果,A1出栈结果入栈;...如果这样看来的话,每一种计算顺序,实际上对应一种 A0-A(n-1) 的入栈出栈情况,于是,此问题和问题2等价;
...
有时候上面几个问题的等价不是那么明显,然而上面的几个问题都可以用同一个递推公式来表示:
C0 = 1 ; Cn = ∑ Ci · C(n-1-i) ; 举例: C0 = 1; C1 = 1; C2 = 2; C3 = 5; C4 = 14; C5 = C0C4 + C1C3 + C2C2 + C3C1 + C4C0 = 14+5+4+5+14 = 42
上面的几个问题都是等价问题,所得的数就是 Catalan 数,可以计算出通项公式(可以使用生成函数法):
Cn = C(n,2n)/(n+1) ,其中C(m,n)是 n 个不同的数中取 m 个数形成的组合数;
注:C5 = 42,介貌似是一个灰常有名的数,可以记一下。