Leetcode刷题--不同的二叉搜索树

LeetCode-95 不同的二叉搜索树

题目

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

示例:

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

思路

从递归入手思考,对1~n中的每一个数 i,都可以当做树的根节点,此时以 i 为根节点的子树个数 f(n)可以表示为 f(n) = f(i-1) * f(n-i),对于 f(i-1) 和 f(n-i)此公式依然适用,因而可以递归的求解。但指数级的时间复杂度显然不是我们想要的。考虑采用动态规划对递归算法进行优化。

我们先来看看递归算法的缺点在哪里:	
为了方便计算,我们让f(0) = 1,另外显然的有f(1) = 1
以计算f(3)为例:
f(3) = f(0) * f(2) + f(1) * f(1) + f(2) * f(0)
	 = f(0) * [f(0) * f(1) + f(1) * f(0)] +  f(1) * f(1) + [f(0) * f(1) + f(1) * f(0)] * f(0)

看到问题了吗。在计算f(3) 的时候我们重复计算了多次f(2),而这是不必要的。
如果我们能通过一个数组记录下所有已经算过的节点,就可以减少计算次数从而达到降低时间复杂度的目的,这便是动态规划。

状态转换方程

经过上述分析可以得出:

f ( x ) = ∑ i = 1 N [ f ( i − 1 ) ∗ f ( n − i ) ] f(x) = \begin{matrix} \sum_{i=1}^N [f(i-1) * f(n-i)] \end{matrix} f(x)=i=1N[f(i1)f(ni)]
其实就是递归的方程,只不过把算过的节点记下来,用的时候直接带就完了

C语言实现

代码很简单

//状态方程 fn = Σi(0~n-1)(fi * f(n-i-1))
int numTrees(int n){
    //初始化dp数组
    int dp[n+1];
    memset(dp, 0, sizeof(dp));
    //注意这里不能用dp[n+1] = {0}

    //初始化前两者
    dp[0] = 1;
    dp[1] = 1;

    for(int i = 2; i < n+1; i++){
        for(int j = 0; j < i; j++){
            dp[i] += dp[j] * dp[i-j-1];
        }
    }
    
    return dp[n];
}

时间复杂度O(n^2) :二重循环
空间复杂度O(n) : 额外维护数组的开销

卡特兰数

实际上二叉搜索树的个数问题,这样的数列被称为卡特兰数(人家早就研究透了),拥有一个通项公式:
C ( 0 ) = 1 C(0) = 1 C(0)=1
C ( n + 1 ) = 2 ( 2 n + 1 ) n + 2 C ( n ) C(n+1) = \frac{2(2n+1)}{n+2}C(n) C(n+1)=n+22(2n+1)C(n)
卡特兰数又称卡塔兰数,卡特兰数是组合数学中一个常出现在各种计数问题中的数列。以比利时的数学家欧仁·查理·卡塔兰 (1814–1894)的名字来命名。
比较常见的应用有:

1.矩阵连乘的括号优化方案
2.出栈次序
一个栈(无穷大)的进栈序列为1,2,3,…,n,有多少个不同的出栈序列?
3.凸多边形三角划分
在一个凸多边形中,通过若干条互不相交的对角线,把这个多边形划分成了若干个三角形。任务是键盘上输入凸多边形的边数n,求不同划分的方案数f(n)

奇怪的数学知识又增加了!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值