时间复杂度:O(n²),空间复杂度:O(n)
解题思路
正常想法是动态规划,但是理解起来可能有一些别扭。如果是笔试就用卡特兰数解决。
我们可以先用递归的方法想一想,如果要想知道n个节点的二叉搜索树种类数,那么是不是应该尝试将所有节点都做一次根节点,然后它左面的节点就会成为它左子树节点,右面的节点就会成为它右子树的节点,这样就有了分而治之的思想。
接下来我们再尝试在它的左子树所有节点中重复以上的步骤,还是先找根节点,然后划分左子树节点和右子树节点,直到左子树或右子树节点数为0或1,说明只有一种情况,然后将左子树的情况数乘右子树的情况数就是根节点的情况数了。这样通过递归我们就能得到总的情况数。
但有没有发现这样递归浪费了许多无用功,也就是计算n个节点有多少种情况时可能第一次递归就计算出2个节点有2种,但是第二次递归其它子树时又算一遍2个节点有几种,这样重复计算会降低时间性能,所以我们考虑将计算过的n个节点情况数记录下来,这样下次就可以直接用而不需要重复计算了。
而这就有了动态规划的思想,先求出2个节点时的情况数,两个节点的情况数依赖于0个节点和1个节点的情况数,求完2个节点再去求3个节点时的情况数,3个节点的情况数依赖于0、1、2个节点的情况数……以此类推就可以求出n个节点时的情况数了。
AC代码
func numTrees(n int) int {
G:=make([]int,n+1)//节点个数为n时的二叉搜索树种类数
G[0],G[1]=1,1//初始化边界值
//从节点数为2开始计算
for i:=2;i<=n;i++{
//第j个数作为根节点
for j:=1;j<=i;j++{
G[i]+=G[j-1]*G[i-j]//如果第j个数作为根节点,那么左子树一共有j-1个节点,右字数一共有i-j个节点,总的种类数就是二者种类数的乘积
}
}
return G[n]
}
感悟
又一次被动态规划打败,不过代码写起来很简单,就看能不能屡明白吧……