Leetcode 96. 不同的二叉搜索树

题目

在这里插入图片描述

解答

解法一:递归

递归直接超时。。

但是本题的最基本思路有了:

  1. 对于每一个结点都尝试作为根节点进行计算不同的左子树的可能数和右子树的可能数。
  2. 最后将所有的可能累加起来即可。

思路明了,剩下的就是优化的事了。

代码
class Solution {
    public int numTrees(int n) {
        return numTrees(1, n);
    }
    
    private int numTrees(int start, int end) {
        if(start > end) {
            return 1;
        }
        
        int res = 0;
        for(int i = start; i <= end; i ++) {
            int left = numTrees(start, i - 1);
            int right = numTrees(i + 1, end);
            res += left * right;
        }
        
        return res;
    }
}
结果

在这里插入图片描述

备忘录优化的代码一

简单的对递归操作加一个备忘录记录已经计算的结果。

复杂度:O(n^2) 时间,O(n^2) 空间。

class Solution {
    public int numTrees(int n) {
        int[][] memo = new int[n + 1][n + 1];
        return numTrees(1, n, memo);
    }
    
    private int numTrees(int start, int end, int[][] memo) {
        if(start > end) return 1;
        if(memo[start][end] != 0) return memo[start][end];
        
        int res = 0;
        for(int i = start; i <= end; i ++) {
            int left = numTrees(start, i - 1, memo);
            int right = numTrees(i + 1, end, memo);
            res += left * right;
        }
        
        memo[start][end] = res;
        return res;
    }
}
结果

在这里插入图片描述

备忘录优化的代码二

可以预见:1 … 3 与 4 … 6 其实构造的树的结构是一样的,只不过值不同而已。

如果只计算数量的话就可以将他们统一化。将空间复杂度降为 O(n)。

复杂度:O(n^2) 时间,O(n) 空间。

class Solution {
    public int numTrees(int n) {
        int[] memo = new int[n + 1];
        return numTrees(n, memo);
    }
    
    private int numTrees(int n, int[] memo) {
        if(n == 0) return 1;
        if(memo[n] != 0) return memo[n];
        
        int res = 0;
        for(int i = 1; i <= n; i ++) {
            int left = numTrees(i - 1, memo);
            int right = numTrees(n - i, memo);
            res += left * right;
        }
        
        memo[n] = res;
        return res;
    }
}
结果

在这里插入图片描述

解法二:动态规划

将备忘录优化二的代码转换成自底向上的方式就是本题动态规划的解。

下述代码的解释:

  1. 第一层 for 循环确定组成二叉树结点的个数。
  2. 第二层 for 循环确定由哪一个结点作为根节点。

复杂度:O(n^2) 时间,O(n) 空间。

代码
class Solution {
    public int numTrees(int n) {
        int[] dp = new int[n + 1];
        dp[0] = 1;
        dp[1] = 1;
        
        for(int i = 2; i <= n; i ++) {
            for(int j = 1; j <= i; j ++) {
                dp[i] += dp[j - 1] * dp[i - j];
            }
        }
        
        return dp[n];
    }
}
结果

在这里插入图片描述

解法三:卡特兰数

分析一下本题:

  1. 假设由 n 个结点组成的不同的二叉搜索树数量为 h(n)。
  2. 那么现在再假设将第 k 个结点作为根节点,可以得到 f(k)=h(k-1)*h(n-k)。
  3. 而每个结点都可能会被作为根节点,也就是说 k 的取值范围是从 1 到 n 的。
  4. 所以可以推出公式:h(n) = f(1) + f(2) + … + f(n)。
  5. 进而满足了卡特兰数递推通式:h(n)= h(0)*h(n-1)+h(1)*h(n-2) + … + h(n-1)h(0) (n>=2)。

所以本题可以使用卡特兰数进行计算!

补充:卡特兰数其前几项为(从第 0 项开始): 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, …

为了计算的方便,我们可以选用以下几个公式任意一个:

  • C0 = 1,Cn+1 = Cn * (4n + 2) / (n + 2)
  • Cn = (2n)! / ((n + 1)! * n !)
  • Cn = C(2n,n) / (n+1) ,此处的 C(2n,n) 是组合数。

此题我选用了第一个公式。

复杂度:O(n) 时间,O(1) 空间。

代码
class Solution {
    public int numTrees(int n) {
        long C = 1;
        for(int i = 0; i < n; i ++) {
            C = C * (4 * i + 2) / (i + 2);
        }
        return (int)C;
    }
}
结果

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值