如何计算所有合法BST
之前写了两篇 BST 算法题相关的⽂章,第一篇讲了中序遍历对 BST 的重要意义,第二篇 写了 BST 的基本操作。
本⽂循序渐进地讲两道题,讲述如何计算所有合法 BST。
96. 不同的二叉搜索树
解法:递归
这就是一个正宗的穷举问题,那么什么方式能够正确地穷举合法 BST 的数量呢?
对于区间[1,2,…,n],我们依次计算每个数字作为根节点的情况,再进行求和即可。例如对于n=5,固定数字3作为根节点,这个前提下能有几种不同的 BST 呢?
固定3为根节点,根据BST特性,其左子树为节点[1,2]的组合,右子树节点为[4,5]的组合,左子树的组合数和右子树的组合数乘积就是3作为根节点时的 BST 个数,等于4。
抽象为递归过程即为,对于任一区间,计算其BST个数,为对以区间内每个数字为根节点的BST个数的总和。对于任一数字根节点,其BST个数为,左区间BST个数(组合数)乘以右区间BST个数(组合数)。
递归过程存在许多重复子问题,可以使用备忘录(代码中mamo数组)来解决。
class Solution:
def numTrees(self, n: int) -> int:
mamo = [[0]*n for _ in range(n)]
# 计算闭区间 [lo, hi] 组成的 BST 个数
def count(lo, hi):
nonlocal mamo
if lo >= hi:
return 1
# 查备忘录
if mamo[lo][hi] != 0:
return mamo[lo][hi]
res = 0
for i in range(lo, hi+1):
# i 的值作为根节点 root
left = count(lo, i-1)
right = count(i+1, hi)
# 左右子树的组合数乘积是 BST 的总数
res += left*right
# 存储备忘录
mamo[lo][hi] = res
return res
# 计算闭区间 [1, n] 组成的 BST 个数
return count(0, n-1)
95. 不同的二叉搜索树2
解法:递归
和上题思路一致,将原问题分解为左右区间的子问题,根据子问题的返回结果递归求解原问题。具体步骤如下:
- 对于给定区间,穷举root节点的所有可能。
- 递归构造出左右子树的所有合法 BST。
- 给root节点穷举所有左右子树的组合。作为结果返回。
class Solution:
def generateTrees(self, n: int) -> List[TreeNode]:
# 备忘录操作
mamo = [[None]*(n+1) for _ in range(n+1)]
# 构造闭区间 [lo, hi] 组成的 BST
def count(lo, hi):
if lo > hi:
return [None]
if lo == hi:
return [TreeNode(lo)]
if mamo[lo][hi]:
return mamo[lo][hi]
result = []
# 1、穷举 root 节点的所有可能
for i in range(lo, hi+1):
# 2、递归构造出左右子树的所有合法 BST。
l_result = count(lo, i-1)
r_result = count(i+1, hi)
# 3、给 root 节点穷举所有左右子树的组合。
for left in l_result:
for right in r_result:
# i 作为根节点 root 的值
root = TreeNode(i)
root.left = left
root.right = right
result.append(root)
mamo[lo][hi] = result
return result
return count(1, n)