在问 · 算法题解

96. 不同的二叉搜索树

中等二叉树动态规划力扣原题 ↗

题目描述

输入整数 n。用值为 1 到 n 的 n 个节点,每个值恰好用一次,搭成二叉搜索树。

二叉搜索树中,每个节点左子树的值都比它小,右子树的值都比它大。

返回结构互不相同的这种树共有多少棵。

示例 1
输入:n = 3
输出:5

解释:以 1、2、3 为根分别有 2、1、2 种,共 5 种。

示例 2
输入:n = 1
输出:1

数据范围

  • 1 <= n <= 19

解题思路:动态规划

设 g[i] 为 i 个节点能组成的 BST 种数,它只取决于节点个数,与具体的值无关。在 1..i 中选 j 作根,左子树由 j-1 个更小的值组成,右子树由 i-j 个更大的值组成,两边独立,所以有 g[j-1]·g[i-j] 种。对所有 j 求和就是 g[i],即卡特兰数。

为什么正确

计算 g[i] 时,g[0..i-1] 都已经是最终值。

复杂度分析

时间 O(n²),空间 O(n)。

易错点

g[0] 必须是 1 而不是 0,否则左右子树有一边为空的情况会被乘成 0;左右子树的数量要相乘,不是相加。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def numTrees(self, n):
        g = [0] * (n + 1)
        g[0] = 1
        for i in range(1, n + 1):
            for j in range(1, i + 1):
                g[i] += g[j - 1] * g[i - j]
        return g[n]

载入编辑器,自己跑一遍 →

其他解法

动态规划从“枚举根节点”推出递推,是自己能想到的做法;认出它是卡特兰数后,可以用 O(n) 公式直接计算。

解法 2:卡特兰数公式 时间 O(n),空间 O(1)

主解法的递推 G(n) = Σ G(j-1)·G(n-j) 正是卡特兰数的定义,而卡特兰数有更简单的递推式:C(0) = 1,C(i+1) = C(i) · 2(2i+1) / (i+2)。从 C(0) 开始连乘 n 次即可得到答案,每一步先乘后除结果仍是整数。

时间从 O(n²) 降到 O(n),空间 O(1)。需要知道卡特兰数的结论,而且中间的乘积会比答案大,C++/Java 要用 64 位整数。

class Solution:
    def numTrees(self, n):
        c = 1
        for i in range(n):
            # C(i+1) = C(i) * 2(2i+1) / (i+2)
            c = c * 2 * (2 * i + 1) // (i + 2)
        return c

二叉树 专题的其他题目

查看 二叉树 全部题目 →