Catalan numbers
标签(空格分隔): 算法
前言
第一次看到 Catalan 数是 leetcode 中的 不同的二叉搜索树 自己做了一阵的树的简单题,来做中等题的时候尝试一下通过率比较高的,没想到也还是完全没有头绪。一查发现原来是 Catalan 数,网上都说学计算机的不会卡特兰数都不好意思说是学计算机的,那我还真的不好意思说自己是学计算机的。就赶紧补一下
问题由来
在一个 n * n 的方格地图中,从一个角到另一个角,不跨越对角线的路径数有多少
思路
假设从 (0, 0)
开始走到 (n, n)
这里每一步只可以有两种走法,U(向上) 或者 R(向右) 。
直接去思考这个问题的答案好像很困难,且没有头绪,那么不妨绕一点远路。首先来求从 (0, 0)
到 (n, n)
一共有多少种走法,显然,是 C(2n, n)
。进而减去那些跨越过了对角线的不合法路径,即:
- Total - Above Diagonal
方法
那么 Above Diagonal ,这个怎么求呢。
嘿嘿嘿,那些伟大的数学家已经替我们想出来了,来观察那些越过了对角线的非法路径
从他们越过了对角线的第一步开始,对他们原本后半段路径画出镜面路径(向上变成向右,向右变成向上),如图中的 粉红色 和 蓝色 ,深紫色 和 红色 ,然而不是巧合(你可以自己去试试),镜面路径的终点都从原来的 (n, n)
变成了 (n - 1, n + 1)
。这意味着什么呢,这意味着 所有的非法路径都可以看成是所有从 (0, 0)
到 (n - 1, n + 1)
的路径映射
那么,我们其实就把 Above Diagonal 求出来了,它就是 C(2n, n + 1)
而卡特兰数就是 Cn = C(2n, n) - C(2n, n+1)
经过化简后的一般形式为:
也满足递推关系:
更快速的计算方法:
应用
卡特兰数有着相当多的应用,在这里对应一开始的出发点 leetcode 中的不同的二叉搜索树,简单地套公式可得:
public class Solution {
public int NumTrees(int n) {
// 利用卡特兰数递推公式计算
if(n < 2) return 1;
long cn = 1;
for(int i = 0; i < n; i++)
{
cn = 2 * cn *(2 * i + 1) / (i + 2);
}
return (int)cn;
}
}
这个代码运行起来比使用第一个递推公式还要慢,大概是因为除法的缘故。但是这个也不是重点,重点是如何在各种场景下灵活的运用卡特兰数。好的,这个问题,我也不知道,以后遇到坑再补