线性动态规划:不同的二叉搜索树(Unique Binary Search Trees)
字数 2322 2025-12-14 11:59:45

线性动态规划:不同的二叉搜索树(Unique Binary Search Trees)

题目描述

给定一个整数 \(n\),表示二叉搜索树(BST)中节点的值由 1 到 \(n\) 组成。要求计算能够生成的所有结构不同的二叉搜索树的数目。不同 BST 定义为结构不同,即树的形态不同(与节点具体值无关,因为 1 到 \(n\) 的 BST 结构唯一对应一种形态)。

示例

  • 输入:\(n = 3\)
  • 输出:5
  • 解释:对于节点值 1, 2, 3,可以生成以下 5 种结构不同的 BST:
   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3

解题步骤

1. 理解问题本质

二叉搜索树(BST)的定义是:左子树的所有节点值 < 根节点值 < 右子树的所有节点值。给定节点值 1 到 \(n\),如果我们选择某个值 \(i\) 作为根节点,那么:

  • 左子树将包含值 1 到 \(i-1\)(共 \(i-1\) 个节点)。
  • 右子树将包含值 \(i+1\)\(n\)(共 \(n-i\) 个节点)。

问题转化为:给定节点数量,计算不同结构的 BST 数量,且只与节点数量有关,与具体值无关(因为 BST 的结构只由节点排列顺序决定)。

2. 定义状态

\(G(n)\) 表示由 \(n\) 个不同节点(值 1 到 \(n\))能生成的不同 BST 的数量。
更一般地,设 \(F(i, n)\) 表示以值 \(i\) 为根节点,节点总数为 \(n\) 的 BST 数量。但注意,由于 BST 结构只与节点数有关,我们可以简化:

  • \(i\) 为根时,左子树节点数为 \(i-1\),右子树节点数为 \(n-i\)
  • 左子树的不同结构数 = \(G(i-1)\)(由 \(i-1\) 个节点生成的 BST 数量)。
  • 右子树的不同结构数 = \(G(n-i)\)(由 \(n-i\) 个节点生成的 BST 数量,但这些节点的值实际上是 \(i+1\)\(n\),结构上与值 1 到 \(n-i\) 的 BST 相同)。

因此,以 \(i\) 为根的 BST 数量 = \(G(i-1) \times G(n-i)\)

3. 推导递推关系

遍历所有可能的根节点 \(i\)(从 1 到 \(n\)),将每种情况的数量累加:

\[G(n) = \sum_{i=1}^{n} G(i-1) \times G(n-i) \]

其中,边界条件:

  • \(G(0) = 1\):空树算一种结构(即左/右子树为空的情况)。
  • \(G(1) = 1\):单个节点只有一种结构。

4. 动态规划计算过程

\(n = 0\) 开始,逐步计算 \(G(1), G(2), \dots, G(n)\)

\(n = 3\) 为例

  1. 初始化 \(G(0) = 1, G(1) = 1\)
  2. 计算 \(G(2)\)
    • 根节点为 1:左子树节点数 0,右子树节点数 1 → \(G(0) \times G(1) = 1 \times 1 = 1\)
    • 根节点为 2:左子树节点数 1,右子树节点数 0 → \(G(1) \times G(0) = 1 \times 1 = 1\)
    • 总和:\(G(2) = 1 + 1 = 2\)
  3. 计算 \(G(3)\)
    • 根节点为 1:左子树节点数 0,右子树节点数 2 → \(G(0) \times G(2) = 1 \times 2 = 2\)
    • 根节点为 2:左子树节点数 1,右子树节点数 1 → \(G(1) \times G(1) = 1 \times 1 = 1\)
    • 根节点为 3:左子树节点数 2,右子树节点数 0 → \(G(2) \times G(0) = 2 \times 1 = 2\)
    • 总和:\(G(3) = 2 + 1 + 2 = 5\)

结果与示例一致。

5. 算法实现

我们可以用一维数组 dp 保存 \(G(k)\) 的值,dp[0] = 1
外层循环 k = 1 to n,内层循环 i = 1 to k,累加 dp[i-1] * dp[k-i]

时间复杂度\(O(n^2)\)(双重循环)。
空间复杂度\(O(n)\)(存储 dp 数组)。

6. 示例完整计算表(\(n = 4\)

  • \(G(0) = 1\)
  • \(G(1) = 1\)
  • \(G(2) = G(0)G(1) + G(1)G(0) = 2\)
  • \(G(3) = G(0)G(2) + G(1)G(1) + G(2)G(0) = 2 + 1 + 2 = 5\)
  • \(G(4) = G(0)G(3) + G(1)G(2) + G(2)G(1) + G(3)G(0) = 5 + 2 + 2 + 5 = 14\)

因此,\(n = 4\) 时答案为 14。

关键点

  • 该递推公式定义的 \(G(n)\) 实际上是卡特兰数(Catalan Number)\(G(n) = \frac{1}{n+1} \binom{2n}{n}\)
  • 动态规划避免重复计算子问题,例如计算 \(G(4)\) 时直接使用已算出的 \(G(0), G(1), G(2), G(3)\)
  • 理解 BST 的结构仅依赖于节点数,是问题简化的核心。

通过以上步骤,我们系统地解决了“不同的二叉搜索树”计数问题。

线性动态规划:不同的二叉搜索树(Unique Binary Search Trees) 题目描述 给定一个整数 \( n \),表示二叉搜索树(BST)中节点的值由 1 到 \( n \) 组成。要求计算能够生成的 所有结构不同的二叉搜索树 的数目。不同 BST 定义为结构不同,即树的形态不同(与节点具体值无关,因为 1 到 \( n \) 的 BST 结构唯一对应一种形态)。 示例 : 输入:\( n = 3 \) 输出:5 解释:对于节点值 1, 2, 3,可以生成以下 5 种结构不同的 BST: 解题步骤 1. 理解问题本质 二叉搜索树(BST)的定义是:左子树的所有节点值 < 根节点值 < 右子树的所有节点值。给定节点值 1 到 \( n \),如果我们选择某个值 \( i \) 作为根节点,那么: 左子树将包含值 1 到 \( i-1 \)(共 \( i-1 \) 个节点)。 右子树将包含值 \( i+1 \) 到 \( n \)(共 \( n-i \) 个节点)。 问题转化为:给定节点数量,计算不同结构的 BST 数量,且 只与节点数量有关,与具体值无关 (因为 BST 的结构只由节点排列顺序决定)。 2. 定义状态 设 \( G(n) \) 表示由 \( n \) 个不同节点(值 1 到 \( n \))能生成的不同 BST 的数量。 更一般地,设 \( F(i, n) \) 表示以值 \( i \) 为根节点,节点总数为 \( n \) 的 BST 数量。但注意,由于 BST 结构只与节点数有关,我们可以简化: 以 \( i \) 为根时,左子树节点数为 \( i-1 \),右子树节点数为 \( n-i \)。 左子树的不同结构数 = \( G(i-1) \)(由 \( i-1 \) 个节点生成的 BST 数量)。 右子树的不同结构数 = \( G(n-i) \)(由 \( n-i \) 个节点生成的 BST 数量,但这些节点的值实际上是 \( i+1 \) 到 \( n \),结构上与值 1 到 \( n-i \) 的 BST 相同)。 因此,以 \( i \) 为根的 BST 数量 = \( G(i-1) \times G(n-i) \)。 3. 推导递推关系 遍历所有可能的根节点 \( i \)(从 1 到 \( n \)),将每种情况的数量累加: \[ G(n) = \sum_ {i=1}^{n} G(i-1) \times G(n-i) \] 其中,边界条件: \( G(0) = 1 \):空树算一种结构(即左/右子树为空的情况)。 \( G(1) = 1 \):单个节点只有一种结构。 4. 动态规划计算过程 从 \( n = 0 \) 开始,逐步计算 \( G(1), G(2), \dots, G(n) \)。 以 \( n = 3 \) 为例 : 初始化 \( G(0) = 1, G(1) = 1 \)。 计算 \( G(2) \): 根节点为 1:左子树节点数 0,右子树节点数 1 → \( G(0) \times G(1) = 1 \times 1 = 1 \)。 根节点为 2:左子树节点数 1,右子树节点数 0 → \( G(1) \times G(0) = 1 \times 1 = 1 \)。 总和:\( G(2) = 1 + 1 = 2 \)。 计算 \( G(3) \): 根节点为 1:左子树节点数 0,右子树节点数 2 → \( G(0) \times G(2) = 1 \times 2 = 2 \)。 根节点为 2:左子树节点数 1,右子树节点数 1 → \( G(1) \times G(1) = 1 \times 1 = 1 \)。 根节点为 3:左子树节点数 2,右子树节点数 0 → \( G(2) \times G(0) = 2 \times 1 = 2 \)。 总和:\( G(3) = 2 + 1 + 2 = 5 \)。 结果与示例一致。 5. 算法实现 我们可以用一维数组 dp 保存 \( G(k) \) 的值, dp[0] = 1 。 外层循环 k = 1 to n ,内层循环 i = 1 to k ,累加 dp[i-1] * dp[k-i] 。 时间复杂度 :\( O(n^2) \)(双重循环)。 空间复杂度 :\( O(n) \)(存储 dp 数组)。 6. 示例完整计算表(\( n = 4 \)) \( G(0) = 1 \) \( G(1) = 1 \) \( G(2) = G(0)G(1) + G(1)G(0) = 2 \) \( G(3) = G(0)G(2) + G(1)G(1) + G(2)G(0) = 2 + 1 + 2 = 5 \) \( G(4) = G(0)G(3) + G(1)G(2) + G(2)G(1) + G(3)G(0) = 5 + 2 + 2 + 5 = 14 \) 因此,\( n = 4 \) 时答案为 14。 关键点 该递推公式定义的 \( G(n) \) 实际上是 卡特兰数(Catalan Number) :\( G(n) = \frac{1}{n+1} \binom{2n}{n} \)。 动态规划避免重复计算子问题,例如计算 \( G(4) \) 时直接使用已算出的 \( G(0), G(1), G(2), G(3) \)。 理解 BST 的结构仅依赖于节点数,是问题简化的核心。 通过以上步骤,我们系统地解决了“不同的二叉搜索树”计数问题。