96:不同的二叉搜索树

问题描述

给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?

示例

输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:

   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3

思路

这题我想用95题的思路,构建出来,然后统计最外层的树的个数。(时间复杂度太高了) 不出意外收获了LTE.
总感觉有什么不对,感觉有什么规律。对于95题,我们是把每个结点都当做根结点,在它左边的结点当做它的左子树的根,在它右边的结点当做它的右子树的根。
这样的话,其实对于固定长度的序列来说,生成的树的数目都是一样的。
也就是说:如果一个序列的长度为3,别管序列中存储的值是什么,那么他们生成的二叉搜索树的数量是确定的。
可见,问题可以分解成规模较小的子问题。因此,我们可以存储并复用子问题的解。为啥?以序列长度为3为例:
这个序列可生成的树数为:
以第一个位置的元素为根,以第二个位置的元素为根,以第三个元素为根。 所有的数加起来。
而以第一个位置的元素为根,则右边两个数是它的右子树。 右子树长度为2,它的可生成的树数为: 以第一个位置的元素为根,以第二个位置的元素为根。加起来。
而这个第一个位置元素为根,第二个元素为右子树的情况,只有一种。
第二个位置为根,第一个元素为左子树的情况,只有一种。
所以我们得到长度为2的序列的树数为2,存储下来。直接给上层使用。

我们用res数组来存储,res[i]为长度为i的序列的二叉搜索树的个数。
总结一下就是,为了以 3 为根从序列 [1, 2, 3, 4, 5, 6, 7] 构建二叉搜索树,我们需要从左子序列 [1, 2] 构建左子树,从右子序列 [4, 5, 6, 7] 构建右子树,然后将它们组合(即笛卡尔积)。而左子序列[1,2]构建的左子树的二叉搜索树个数等于res[2], [4, 5, 6, 7] 对应res[4]。乘起来即可。为啥呢?左子树有这么多种,右子树有这么多种,任意组合就是个树。 所以是笛卡尔积(左边集合中的所有值分别于右边集合中的所有值都相乘一遍)。
(方法一)

方法一在数学里叫做卡特兰数。我们可以直接用公式来做。

  • C0 = 1
  • 在这里插入图片描述

根据这两个公式,我们可以推出来。(方法二)

方法一

public int numTrees1(int n) {
        int[] res = new int[n+1];
        res[0] = 1;
        res[1] = 1;
        for(int curIndex = 2; curIndex < res.length; curIndex++){
            for(int j = 0; j < curIndex; j++){
                res[curIndex] += res[j]*res[curIndex-j-1];
            }
        }
        return res[n];
    }

方法二

public int numTrees(int n){
        // 防止公式计算过程中溢出,python不需要这样
        long res = 1;
        for(int i = 0; i < n; i++){
            res = 2*(2*i+1)*res/(i+2);
        }
        return (int)res;
    }
发布了464 篇原创文章 · 获赞 21 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_41687289/article/details/105306994