卡特兰数在数据结构中的运用
问题引入
问题背景
总共有2n次选择,每次可以选A或选B,选A和B的总次数均为n,要求每个前缀序列中A都不少于B,求这样的序列的个数。
卡特兰数
这里首先给出,上面问题的结果为卡特兰数。
具体的证明过程参见后文的“卡特兰数在数据结构中的应用-进出栈序列的可能性问题”部分。
另一种理解卡特兰数的方法
从递推公式中可以看出,只要问题符合以下的性质,就可以考虑使用卡特兰数进行表示。
- 总问题通过不同的划分方法分别分割成两个子问题,子问题也满足此性质
- 两个子问题求解结果之积就是在这种划分下的结果
- 最终结果就是不同划分下的结果之和
上面所说的“问题背景”就符合这种性质。
- 假设最后出栈的数是第 i i i 个数,那么它入栈之前一定有 i − 1 i-1 i−1 个数字完成了进栈出栈(子问题1),它入栈之后也一定有 n − i n-i n−i 个数字完成了进栈出栈(子问题2)
- 固定这个数最后出栈时,结果为子问题1和子问题2结果的乘积
- 每个数都可能作为最后一个数出栈,因此最终结果就是不同划分下的结果之和
卡特兰数的递归定义
f ( n ) = ∑ k = 0 n − 1 f ( k ) f ( n − 1 − k ) = 4 n − 2 n + 1 f ( n − 1 ) , n ≥ 1 f ( 0 ) = 1 f(n) = \sum\limits_{k=0}^{n-1} f(k) f(n-1-k) = \frac{4n-2}{n+1}f(n-1),n\ge 1 \\ f(0) = 1 f(n)=k=0∑n−1f(k)f(n−1−k)=n+14n−2f(n−1),n≥1f(0)=1
或
f
(
n
+
1
)
=
∑
i
=
0
n
f
(
i
)
f
(
n
−
i
)
f
(
0
)
=
1
f(n+1) = \sum\limits_{i=0}^{n} f(i) f(n-i) \\ f(0) = 1
f(n+1)=i=0∑nf(i)f(n−i)f(0)=1
卡特兰数的通项公式
C a t a l a n ( n ) = 1 n + 1 C 2 n n = C 2 n n − C 2 n n − 1 {\rm Catalan} (n) = \frac{1}{n+1} C_{2n}^{n} = C_{2n}^n - C_{2n}^{n-1} Catalan(n)=n+11C2nn=C2nn−C2nn−1
至于如何相互推导,可以用生成函数法,但是这样写下去就没完了(手动狗头)
这里放一个链接:神奇的卡塔兰(Catalan)数 - 知乎 (zhihu.com)
卡特兰数在数据结构中的应用
进出栈序列的可能性问题
进栈记为+1,出栈记为-1
合法序列满足总和为0,且每个前缀的和大于等于0
正好是卡特兰数“使用背景”中提到的条件
因此n个元素合法的进出栈序列有 1 n + 1 C 2 n n \frac{1}{n+1} C_{2n}^{n} n+11C2nn 种
结论推导
通过补集思想进行计算
最终推导得出 C 2 n n − C 2 n n − 1 C_{2n}^n - C_{2n}^{n-1} C2nn−C2nn−1 的结果
- 将进出栈表示为+1、-1:如果有n个元素进行进出栈操作,则必须操作2n次,且其中必须含有n次进栈和n次出栈,所以如果通过-1、+1来表示进栈和出栈的操作,那么所有操作的和必然为0;
- 非法序列:通过组合数 C 2 n n C_{2n}^n C2nn 可以计算出所有操作的为0的进出栈序列,但是其中混有一些非法的序列,比如说在n=3时,序列:-1,+1,-1,+1,+1,-1,这个序列的第三项是在空栈的时候进行出栈操作,显然是错误的,所以需要排除这类的序列。
- 前缀和:在排除非法序列之前,先引进了一种前缀和的概念,前缀和就是一个长度k序列,前1项、前2项…前k项的和。分析一下,对于上一条中满足所有操作和为0序列,根据栈的性质,一个序列前缀和只要出现小于0的数,则可以判定这个序列是非法序列,所以只需要把这种具有前缀和小于0的序列剔除掉,那么剩下的序列就是合法的进出栈方式。
- 第一个值为-1的前缀和:一个进出栈序列如果出现值小于0的前缀和,就肯定能找到找到第一个值为-1的前缀和,如序列:-1,+1,-1,+1,+1,-1,其中-1、+1、-1这个前缀和就是第一个值为-1的前缀和,通过这个前缀和就可以把序列分为两部分,其中第一部分-1,+1,-1,第二部分+1,+1,-1,第一部分的-1数量比+1多1,第二部分的+1数量比-1多1,此时将第一部分全部取反,得到序列+1,-1,+1,+1,+1,-1,此时在整个序列中+1的个数比-1的个数多了2个。所有的非法序列都符合这个规律,故将其推广。设A是包含长度为2n的非法序列的集合,对A中的所有元素第一个值为-1的前缀和取反。可以得到集合B,B中元素的序列中+1的数量比-1的数量多两个,+1的数量是n+1,-1的数量是n-1,且总能找到一个值为+1的前缀和。
- 对于B中元素的数量是容易计算的,B中的元素要满足两个条件,一是有n+1个+1,n-1个-1,二是总能找到一个值为+1的前缀和,分析一下,实际上只要满足第一个条件就必定满足第二个条件,因为+1的数量总比-1多1(最极端的情况是所有-1都排前面,但这个时候也满足第一个条件),所以只要计算满足第一个条件序列的就可以计算出B中的元素数量,通过组合数 C 2 n n + 1 C_{2n}^{n+1} C2nn+1 就可以算出。
- 现在只要证明A中元素的数量和B中元素的数量相等就可以了,实际上在A中任意元素的第一个值为-1的前缀和取反,都可以在B中找到唯一的一个元素与之对应,同理,在B中对任意的元素的第一个值为+1的前缀和取反,都可以在A中找到唯一的元素与之对应,故A与B中的元素是一一对应的关系,所以A中元素的数量就是 C 2 n n + 1 C_{2n}^{n+1} C2nn+1 。
- 所以最后就是用总的元素数 C 2 n n C_{2n}^n C2nn 减去非法序列数 C 2 n n + 1 C_{2n}^{n+1} C2nn+1 ,最后就可以得到所有的可能进出栈序列数 1 n + 1 C 2 n n \frac{1}{n+1} C_{2n}^{n} n+11C2nn 。
注:这一段内容参考了BV1wr4y1q7qw的Peter9997的评论,如有侵权可联系删除
与栈有关的拓展应用:括号匹配
把左括号看成+1,把右括号看成-1即可
给定结点数求二叉树的形态数
n n n 个结点的二叉树有几种形态
f
(
0
)
=
1
f
(
1
)
=
1
f
(
2
)
=
2
f
(
3
)
=
4
.
.
.
f
(
n
)
=
f
(
0
)
f
(
n
−
1
)
+
f
(
1
)
f
(
n
−
2
)
+
.
.
.
+
f
(
n
−
1
)
f
(
0
)
f
(
n
)
=
∑
i
=
0
n
−
1
f
(
i
)
f
(
n
−
1
−
i
)
f(0) = 1\\ f(1) = 1\\ f(2) = 2\\ f(3) = 4\\ ...\\ f(n) = f(0)f(n-1) + f(1)f(n-2) + ...+ f(n-1)f(0) \\f(n)= \sum\limits_{i=0}^{n-1}f(i)f(n-1-i)
f(0)=1f(1)=1f(2)=2f(3)=4...f(n)=f(0)f(n−1)+f(1)f(n−2)+...+f(n−1)f(0)f(n)=i=0∑n−1f(i)f(n−1−i)
选择一个节点作为根节点,其左子树可以有0, 1, …, n-1个结点,其余结点在右子树上
根节点固定,形态数 = 左子树的形态数 * 右子树的形态数
把n种不同根节点的情形加起来即可
给定结点数求满二叉树的形态数
预备知识:
满二叉树的定义本质就是说没有度为1的结点
度为0的结点个数记为 n 0 n_0 n0,度为1的结点个数记为 n 1 n_1 n1,度为2的结点个数记为 n 2 n_2 n2
只要是二叉树,就有 n 0 = n 2 + 1 n_0 = n_2 + 1 n0=n2+1
对满二叉树进行深度优先遍历,向左为+1,向右为-1
因此如果有n个度为2的非叶结点,就需要向左向右各进行一次访问子树操作
而且对于同一个根节点,向左操作一定在向右操作之前
这就符合了卡特兰数的“应用背景”
因此形态数为 C a t a l a n ( n 2 ) {\rm Catalan} (n_2) Catalan(n2)
卡特兰数在实际问题中的应用
-
有2n个人排成一行进入剧场。入场费5元。
其中只有n个人有一张5元钞票,另外n人只有10元钞票,剧院无其它钞票。
问有多少中方法使得只要有10元的人买票,售票处就有5元的钞票找零?
(将持5元者到达视作将5元入栈,持10元者到达视作使栈中某5元出栈)
-
求将一个凸多边形区域分成三角形区域的方法数
(固定一条边,然后找不同的顶点与这条边构成三角形,这个三角形将把这个n边形划分为i+1边形和n-i边形,记 h n h_n hn为n+1边形的划分方法,则有
h n = ∑ k = 1 n − 1 h k h n − k h 1 = 1 h_n = \sum\limits_{k=1}^{n-1} h_k h_{n-k}\\ h_1 = 1 hn=k=1∑n−1hkhn−kh1=1
这个式子的结果也是卡特兰数,但首项可能有点不一样,可以记住五边形有5种划分) -
在n×n的网格上,每次只能向右或向上走一格,在不穿越网格主对角线的情况下,从左下角(0,0)走到右上角(n,n)的不同路径计数。
(每一步都可以选择时向右走或者向上走,向右和向上的步数相同,且规定前缀序列中向右步数不小于向上步数)
-
在圆上选择2n个点,将这些点成对连接起来,使得所得到的n条线段不相交的方法数。
(沿着圆周看,弦的起始点记为+1,终点记为-1,且要求前缀和不小于0——卡特兰数)
在思考这个问题时,注意不要想偏,我们是要求方法数的,只要抽象出的情况和实际问题的情况数一一对应即可。当你想“为什么前缀和不小于0可以确保弦不相交”这个问题时,请赶紧停下来,否则就可能像我一样让一下午的时间悄悄溜走。
事实是,这样的逻辑抽象并不能确保弦不相交,但如果我们规定弦是不相交的,就能使得这种+1和-1所组成的一个序列刚好对应一种确定的生成弦的方法,因此两个问题的情况是一一对应的,情况数是相等的。
如果弦可以相交的话,那么一个+1-1的序列就可以连出好多种弦来,这时候就没什么意义了。也就是说,正是因为规定了弦不能相交,我们才可以把这两种情形对应起来。
参考资料
n个节点的二叉树有多少种形态(Catalan数)_n个结点的二叉树有几种形态-CSDN博客