解法1:
这道题和95题 Unique Binary Search Trees II很像,但是解题思路却相差很多。先用上一道题的思路按照递归写了一下,最后TLE超时了,后来想了一下超时原因,发现这道题由于只用输出个数,不用输出具体的树,所以右边界和左边界的差值相同的子树,数量都是相同的,所以就可以打个表,把已经算过的右边界和左边界的差值记录下来,下次再遇到相同的范围时,可以直接从表中获得结果,不用再回溯了。
需要注意在为表赋初始值时,一定要注意这个初始值能否真正到达。最开始我设置了result_list[1]=2,并没有考虑到n=1的情况,WA了一发,以后一定要注意。
class Solution:
def numTrees(self, n: int) -> int:
if n <= 0:
return 0
# 用于打表,索引是右边界和左边界的差值
result_list = [0 for i in range(n)]
result_list[0] = 1
def recurse(begin: int, end: int) -> int:
if begin >= end:
return 1
if result_list[end-begin] != 0:
return result_list[end-begin]
result = 0
for i in range(begin, end+1):
left = recurse(begin, i-1)
right = recurse(i+1, end)
result += left*right
result_list[end-begin] = result
return result
return recurse(1, n)
解法2:
在解法1通过之后,看了一下题解,发现这道题可以用动态规划来解。因为一个树可以由左二叉搜索子树和右二叉搜索子树构成,而且相同节点个数的二叉搜索子树的个数相同。设dp[i]表示有i个节点数的二叉搜索树个数,而由于左子树和右子树的节点数都比i少,那么左右子树的二叉搜索树个数就已经算出来,可以直接用在dp[i]的计算上。以有3个节点的二叉搜索树为例:
dp[3]=dp[0]*dp[2]+dp[1]*dp[1]+dp[2]*dp[0]
所以动态规划方程为:
d
p
[
i
]
=
∑
j
=
0
i
d
p
[
j
]
∗
d
p
[
i
−
j
−
1
]
dp[i]=\sum^i_{j=0}{dp[j]*dp[i-j-1]}
dp[i]=j=0∑idp[j]∗dp[i−j−1]
其中j是该搜索树左子树的节点个数。
需要注意dp[0]*dp[2]和dp[2]*dp[0]是对称的,值是一样的,所以可以将上式改为:
dp[3]=2*dp[0]*dp[2]+dp[1]*dp[1]
故,整体的代码如下:
class Solution:
def numTrees(self, n: int) -> int:
if n <= 0:
return 0
if n == 1:
return 1
dp = [0 for i in range(n+1)]
dp[0] = 1
dp[1] = 1
for i in range(2, n+1):
for j in range(i//2):
dp[i] += 2*(dp[j]*dp[i-j-1])
if i%2:
dp[i] += dp[i//2]*dp[i//2]
return dp[n]
解法3:
官方题解中说这个其实是卡特兰数,公式为:

有了公式之后,按照公式写代码就好了,没有什么难度,所以此处直接把题解拷贝在了下面,没有自己重新写。
class Solution(object):
def numTrees(self, n):
"""
:type n: int
:rtype: int
"""
C = 1
for i in range(0, n):
C = C * 2*(2*i+1)/(i+2)
return int(C)
这篇博客详细介绍了LeetCode第96题的三种解法,包括利用上一题思路避免回溯以优化时间复杂度,使用动态规划求解,以及揭示问题与卡特兰数的联系。解法1通过建立查找表减少重复计算,解法2利用动态规划的递推公式解决,解法3介绍卡特兰数公式直接求解。
438

被折叠的 条评论
为什么被折叠?



