卡特兰数及括号正确匹配个数问题解释

今天碰到这样一个问题:

 n对括号正确匹配组成的字符串数,例如

1对括号:()                                              1种可能

2对括号:()()                                            1种可能

 3对括号:((())) ()(()) ()()() (())() (()())       5种可能

那么问题:n对括号有多少种正确配对的可能?


网上搜索了下,原来是卡特兰数问题。

百度百科解释:卡特兰数又称卡塔兰数,英文名Catalan number,是组合数学中一个常出现在各种计数问题中出现的数列。由以比利时的数学家欧仁·查理·卡塔兰 (1814–1894)命名,其前几项为 : 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, ...

公式:卡特兰数有固定的解法公式,以后碰到可以直接用公式来求解。(以下这4种计算等价)

令h(0)=1,h(1)=1,catalan数满足递推式  :
公式1:h(n)= h(0)*h(n-1)+h(1)*h(n-2) + ... + h(n-1)h(0) (n>=2)
例如:h(2)=h(0)*h(1)+h(1)*h(0)=1*1+1*1=2
h(3)=h(0)*h(2)+h(1)*h(1)+h(2)*h(0)=1*2+1*1+2*1=5
另类递推式:
公式2:h(n)=h(n-1)*(4*n-2)/(n+1);
递推关系的解为:
公式3:h(n)=C(2n,n)/(n+1) (n=0,1,2,...)
递推关系的另类解为:
公式4:h(n)=c(2n,n)-c(2n,n+1)(n=0,1,2,...)
常见卡特兰数应用:
(1)一个(无穷大)的 进栈 序列为1,2,3,…,n,有多少个不同的 出栈 序列
(2)买票找零:有2n个人排成一行进入剧场。入场费5元。其中只有n个人有一张5元钞票,另外n人只有10元钞票,剧院无其它钞票,问有多少中方法使得只要有10元的人买票,售票处就有5元的钞票找零?
(3)

凸多边形三角划分在一个凸多边形,通过若干条互不相交的对角线,把这个多边形划分成了若干个三角形。任务是键盘上输入凸多边形的边数n,求不同划分的方案数f(n)。比如当n=6时,f(6)=14


(4)道路选择问题:一位大城市的律师在她住所以北n个街区和以东n个街区处工作。每天她走2n个街区去上班。如果她从不穿越(但可以碰到)从家到办公室的对角线,那么有多少条可能的道路?

卡特兰数 - lz_666888 - lz_666888的博客


(5)长度为 2n的 Dyck words的数量。 Dyck words是由 n个 X和 n个 Y组成的字符串,并且从左往右数, Y的数量不超过 X。

(6) 拥有 n+1 个叶子节点的二叉树的数量。例如 4个叶子节点的所有二叉树形态:

卡特兰数 - lz_666888 - lz_666888的博客

(7)圆桌握手问题: 圆桌周围有 2n个人,他们两两握手,但没有交叉的方案数。

(8)本文的“n对括号正确匹配组成的字符串数”问题。

虽然同属于卡特兰数问题,但是用同样的方法解释似乎行不通(看百度百科上的解释:关于出栈的问题。还是比较容易理解的,但是我却不能通过它理解本题)

最后发现是《编程之美2》这本书上的原题。其解释如下:

分析

    “卡特兰数”除了可以使用公式计算,也可以采用“分级排列法”来求解。以 n对括弧的合法匹配为例,对于一个序列 (()而言,有两个左括弧,和一个右括弧,可以看成“抵消了一对括弧,还剩下一个左括弧等待抵消”,那么说明还可以在末尾增加一个右括弧,或者一个左括弧,没有左括弧剩余的时候,不能添加右括弧。

    由此,问题可以理解为,总共 2n个括弧,求 1~2n级的情况,第 i 级保存所有剩余 i 个左括号的排列方案数。 1~8级的计算过程如下表:

这里,我搞了很久才折腾明白,行1-8表示级数--括弧个数,列0-8表示不完整匹配左括号个数。红色为博主解释,其他为原文

卡特兰数 - lz_666888 - lz_666888的博客

比如:1级: 只有一个,只能是左括号(。右括号不能先出现啊。

                      不完整左括号个数为  1个。                      (

             2级:由1级的左括号(,如果给它右括号,则完整匹配;此时不完整匹配左括号个数为0,因此在0这一列下为1;

                                                      如果给再给它左括号,则不完整匹配左括号个数为2,只有一种情况: ((         个数为1

                                                      不完整匹配左括号个数为1这种情况不存在!!!因此不填。

             3级:一定是不完整的,所以不完整匹配左括号个数为0 的下方  空缺;

                       由2级可知,原来有完整匹配 1个”()",   现在又增加了1个括弧,那么只能又出现了一个不完整匹配左括号

                                            原来有不完整匹配左括号2个“((",现在如果增加一个右括号,则不完整匹配左括号个数为1的情况+1

                                                                                                              如果增加一个左括号,则不完整匹配左括号个数为3的情况+1

       现在明白为什么下一级的个数为上一级相邻两边相加。比如3级 1列(0列起始)下=2级0列+2级2列。

依次类推:

        4级:原来3级中有2个不完整1次括弧,那么4级中就有   完整匹配    2次。

                 4级中不完整2次括弧=3级的不完整1次+3级的不完整3次。

                4级的不完整4次括弧 只能是"(((("全左括号,所以每行最后一个数字为1,表示全左括号。

over


    计算过程解释如下: 1级:只能放 1个“(”; 2级:可以在一级末尾增加一个“)”或者一个“ (”

以后每级计算时,如果遇到剩余 n>0个“(”的方案,可以在末尾增加一个“ (”或者“ )”进入下一级;遇到剩余 n=0个“(”的方案,可以在末尾增加一个“ (”进入下一级。

奇数级只能包含剩余奇数个“(”的排列方案

偶数级只能包含剩余偶数个“(”的排列方案

从表中可以看出,灰色部分可以不用计算。

解法

关键代码为:

double Catalan(int n) { if (n == 0) return 1; for (int i = 2; i <= 2 * n; i++) { var m = i <= n ? i : 2 * n + 1 - i; for (int j = (i - 1) & 1; j <= m; j += 2) { if (j > 0) arr[j - 1] += arr[j]; if (j < n) arr[j + 1] += arr[j]; arr[j] = 0; } } return arr[0]; }

 

其中:

n为 Cn中的 n;

arr = new double[n + 1];//arr[i]代表有 k个括弧的时候,剩余 "("个数为 i的排列方案个数 arr[1] = 1;

 

讨论

算法复杂度为 卡特兰数 - lz_666888 - lz_666888的博客 = O(n^2),空间复杂度为 O(n+1)。相对于利用公式计算而言,此方法的优势在于——没有乘除法,只有加法。






你可能感兴趣的:(算法)