🌸题目
🍁给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?
示例:
1 | 输入: 3 |
🌸分析
给定一个有序序列1..n,为了构建出一棵二叉搜索树,我们可以遍历每个数字
i,将该数字作为树根,所以如果求1..n的所有可能。
我们只需要把1作为根节点, [] 空作为左子树, [2 .. n]的所有可能作为右
子树。2作为根节点,[1] 作伪左子树, [3..n]的所有可能作为右子树。
3作为根节点,[1, 2 ]的所有可能作为左子树,[4 … n]的所有可能作为右子树,然后左子树和右子树两两组合。
4作为根节点[1 ,2,3]的所有可能作为佐子树,[5 .. n]的所有可能作为右子树,然后佐子树和右子树两两组合。
…….
n作为根节点,[ 1…. n]的所有可能作为左子树,[]作为右子树。
由此可见,原问题可以分解成规模较小的两个子问题,子问题的解可以复用。
因此,我们可以想到使用递归、动态规划来求解本题。
🌸解法一:递归搜索
按照以上分析的思路,假设n个节点存在二叉排序树的个数是G(n),令f(i)为以i为根的二叉搜索树的个数,当找到其中一个数
i
的左子树可能的数量G(i - 1),右子树可能的数量是G(n - i),那么f(i) = G(i - 1) * G(n - i)
1 | public int numTrees(int n) { |
🌸解法二: 记忆化递归搜索
由于递归的分叉,所以会导致很多重复解的计算,所以使用
memoization
技术,把递归过程中求出的解保存起来,第二次需要的时候直接拿即可。
1 | public int numTrees2(int n) { |
🌸解法三:动态规划
根据前面的解法一定义:
G(n)= f(1)+ f(2)+ f(3)+ f(4)+...+ f(n)
f(i)=G(i-1)*G(n-i)
G(n) 从f(1…n)得到,f(i) 递归依赖于G(n),)G(n) 和序列的内容无关,只和序列的长度有关
到此,我们从小到大计算G函数即可,因为G(n)的值依赖于G(0)…G(n- 1)。
java
1 | public int numTrees3(int n) { |
🌸解法三:动态规划空间优化
源自HeII0W0rId,可移步
🌸解法四: 卡塔兰公式
综合G(n)、f(i) 的 公式可以得到
G(n)= G(0)* G(n-1)+G(1)*(n-2)+...+ G(n-1)*G(0)
这就觉得这个公式挺眼熟的。
下面给出卡塔兰公式的定义
令
h(0)=1,h(1)=1
,卡塔兰数满足递归式:h(n)= h(0)*h(n-1) + h(1)*h(n-2) + .. + h(n-1)h(0)
(其中n>=2),这是n阶递推关系;
还可以化简为1阶递推关系:如h(n)=(4n-2)/(n+1)*h(n-1)(n>1) h(0)=1
该递推关系的解为:
$$
h(n)=\frac{C(2n,n)}{n+1}=\frac{P(2n,n)}{(n+1)!}=\frac{(2n!)}{(n!*(n+1)!)}(n=1,2,3,…)
$$$$
C_n = \frac{(2n)!}{(n+1)!n!}
$$$$
C_0=1, C_{n+1} = \frac{2(2n+1)}{n+2}C_n
$$
1 | public int numTrees5(int n) { |