1. 问题描述:
给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?
输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/unique-binary-search-trees
2. 思路分析:
① 一开始的时候想到使用递归的思路解决,可以使用递归创建出二叉搜索树:以当前的i作为根节点进行递归创建,比当前的根节点小那么递归左子树,比当前的根节点大那么递归右子树,但是后面发现对于某些情况会重复进行递归,比如n = 3的时候,以2为根节点那么只有一种二叉搜索树,但是因为比当前的根节点小就递归左子树,比根节点大就递归右子树最后,没有任何的限制条件所以会导致重复递归(第一次的时候是先递归2的左子树1,然后递归右子树3,第二次是递归2的右子树3然后递归左子树1但是这两种二叉树都是一样的)
2
/ \
1 3
所以这样写是不正确的
② 在官方的题解中使用的是递推的方式,其中的策略与我之前的想法是一样的,核心也是以i作为当前的根节点来进行递推,并且以当前的i作为根节点也表示当前的二叉树是有i个节点的,比如n = 3,我们可以尝试以1为根节点,2为根节点,3为根节点下所形成的的二叉搜索树的个数然后累加起来,以i为根节点这样就将n分为了i - 1个节点与 n - 1个节点的两棵子树,i - 1个节点的二叉搜索树与n - 1个节点的二叉搜索树是由之前的节点数为0与1的时候一步步往下推导出来的,而以i为根节点形成的二叉搜索树的数目是由其节点数目为i - 1与 n - 1的左右子树组合形成的,所以左右子树组合的数目为两个数目相乘的结果,所以由分析可以得到官网中的公式:
F(i, n)表示的是以节点数目为i为根节点所形成的二叉树的数目,G(i - 1)与G(n - i)表示的是以节点数目i - 1与n - 1的左右子树的组合数目(左子树有i种,右子树有j种,所以最终形成的数目肯定是i * j)
所以由n个节点的二叉树我们是可以以1,2,3...n为根节点进行累加求得最终的结果的,也就是可以得到官网中的公式:
所以使用两层循环就可以解决了,第一层循环表示以当前节点的个数,第二层循环表示在当前节点数目i的情况下以1,2,3... i为根节点的所形成的二叉搜索树的数目,最后将其累加求和得到的就是以当前的节点的数目的二叉搜索树的数目
③ 上面的第二个公式其实在数学上是卡塔兰数表示的公式:
3. 代码如下:
官网代码:
class Solution:
def numTrees(self, n):
G = [0]*(n+1)
G[0], G[1] = 1, 1
for i in range(2, n+1):
for j in range(1, i+1):
G[i] += G[j-1] * G[i-j]
return G[n]
卡塔兰数公式:
class Solution:
def numTrees(self, n: int) -> int:
return int(math.factorial(2*n)/math.factorial(n+1)/math.factorial(n))