标签:树、二叉搜索树、数学、动态规划、二叉树 难度:中等 描述 :给定一个整数 n n n 。
要求 :求以 1 1 1 到 n n n 为节点构成的「二叉搜索树」有多少种?
说明 :
1 ≤ n ≤ 19 1 \le n \le 19 1 ≤ n ≤ 19 。示例 :
一棵搜索二叉树的左、右子树,要么也是搜索二叉树,要么就是空树。
如果定义 f [ i ] f[i] f [ i ] 表示以 i i i 为根的二叉搜索树个数,定义 g ( i ) g(i) g ( i ) 表示 i i i 个节点可以构成的二叉搜索树个数,则有:
g ( i ) = f ( 1 ) + f ( 2 ) + f ( 3 ) + … + f ( i ) g(i) = f(1) + f(2) + f(3) + … + f(i) g ( i ) = f ( 1 ) + f ( 2 ) + f ( 3 ) + … + f ( i ) 。其中当 i i i 为根节点时,则用 ( 1 , 2 , … , i − 1 ) (1, 2, …, i - 1) ( 1 , 2 , … , i − 1 ) 共 i − 1 i - 1 i − 1 个节点去递归构建左子搜索二叉树,用 ( i + 1 , i + 2 , … , n ) (i + 1, i + 2, …, n) ( i + 1 , i + 2 , … , n ) 共 n − i n - i n − i 个节点去递归构建右子搜索树。则有:
f ( i ) = g ( i − 1 ) × g ( n − i ) f(i) = g(i - 1) \times g(n - i) f ( i ) = g ( i − 1 ) × g ( n − i ) 。综合上面两个式子 { g ( i ) = f ( 1 ) + f ( 2 ) + f ( 3 ) + … + f ( i ) f ( i ) = g ( i − 1 ) × g ( n − i ) \begin{cases} g(i) = f(1) + f(2) + f(3) + … + f(i) \cr f(i) = g(i - 1) \times g(n - i) \end{cases} { g ( i ) = f ( 1 ) + f ( 2 ) + f ( 3 ) + … + f ( i ) f ( i ) = g ( i − 1 ) × g ( n − i ) 可得出:
g ( n ) = g ( 0 ) × g ( n − 1 ) + g ( 1 ) × g ( n − 2 ) + … + g ( n − 1 ) × g ( 0 ) g(n) = g(0) \times g(n - 1) + g(1) \times g(n - 2) + … + g(n - 1) \times g(0) g ( n ) = g ( 0 ) × g ( n − 1 ) + g ( 1 ) × g ( n − 2 ) + … + g ( n − 1 ) × g ( 0 ) 。将 n n n 换为 i i i ,可变为:
g ( i ) = g ( 0 ) × g ( i − 1 ) + g ( 1 ) × g ( i − 2 ) + … + g ( i − 1 ) × g ( 0 ) g(i) = g(0) \times g(i - 1) + g(1) \times g(i - 2) + … + g(i - 1) \times g(0) g ( i ) = g ( 0 ) × g ( i − 1 ) + g ( 1 ) × g ( i − 2 ) + … + g ( i − 1 ) × g ( 0 ) 。再转换一下,可变为:
g ( i ) = ∑ 1 ≤ j ≤ i { g ( j − 1 ) × g ( i − j ) } g(i) = \sum_{1 \le j \le i} \lbrace g(j - 1) \times g(i - j) \rbrace g ( i ) = ∑ 1 ≤ j ≤ i { g ( j − 1 ) × g ( i − j )} 。则我们可以通过动态规划的方法,递推求解 g ( i ) g(i) g ( i ) ,并求解出 g ( n ) g(n) g ( n ) 。具体步骤如下:
按照根节点的编号进行阶段划分。
定义状态 d p [ i ] dp[i] d p [ i ] 表示为: i i i 个节点可以构成的二叉搜索树个数。
d p [ i ] = ∑ 1 ≤ j ≤ i { d p [ j − 1 ] × d p [ i − j ] } dp[i] = \sum_{1 \le j \le i} \lbrace dp[j - 1] \times dp[i - j] \rbrace d p [ i ] = ∑ 1 ≤ j ≤ i { d p [ j − 1 ] × d p [ i − j ]}
0 0 0 个节点可以构成的二叉搜索树个数为 1 1 1 (空树),即 d p [ 0 ] = 1 dp[0] = 1 d p [ 0 ] = 1 。根据我们之前定义的状态,d p [ i ] dp[i] d p [ i ] 表示为: i i i 个节点可以构成的二叉搜索树个数。。 所以最终结果为 d p [ n ] dp[n] d p [ n ] 。
class Solution :
def numTrees ( self , n : int ) -> int :
dp = [ 0 for _ in range (n + 1 )]
dp[ 0 ] = 1
for i in range ( 1 , n + 1 ):
for j in range ( 1 , i + 1 ):
dp[i] += dp[j - 1 ] * dp[i - j]
return dp[n]
时间复杂度 :O ( n 2 ) O(n^2) O ( n 2 ) 。空间复杂度 :O ( n ) O(n) O ( n ) 。