1、题目
2、题解
跟昨天一样又是树,还是一棵新树,来简单了解下二叉搜索树,它是满足如下条件的二叉树
- 左子树所有节点小于根节点
- 右子树所有节点大于根节点
- 左右子树均为二叉搜索树
脑海中首先的想法是从1到n遍历整数n,每个遍历到的数字均可能作为数的根节点,由此依次遍历,递归无疑是最适合的方法
- 递归函数的参数:整数start,end,表示树的节点范围
- 递归出口:树仅有一个节点或没有节点 start>=end
- 递归内容:确定当前树的节点,以及当前节点对应树的个数为 “左子树个数*右子树个数”
最后实现代码如下:
func numTrees(n int) int {
var searchTree func(start, end int) int
searchTree = func(start, end int) int{
res:=0
if start>=end{
return 1
}
for i:=start; i<=end; i++ {
res+=searchTree(start, i-1)*searchTree(i+1, end)
// fmt.Println("r:",i," l:",l," r:",r)
}
return res
}
return searchTree(1, n)
}
以上代码虽然通过了测试,但是在提交中却显示时间超出了限制(n<=19)
莫得办法,只能想办法降低时间复杂度了,因为递归通常能够通过 循环+增加空间消耗 代替,而递归较为费时,尝试将递归改遍历循环,那就需要构建存储空间,保存节点数为i (0<=i<=n) 时的子树个数,这样一想,不就是动态规划吗😂,难道算法也是个圈吗,实现代码如下
func numTrees(n int) int {
dp := make([]int, n+1)
dp[0],dp[1] = 1, 1
for i:=2; i<=n; i++{
for j:=1; j<=i; j++{
dp[i]+=dp[j-1]*dp[i-j]
}
}
return dp[n]
}
提交成功,提交结果如下
看题解之后,发现还有别的思路,搜索树的个数满足卡塔兰数Cn
C0=1,Cn+1 = 2Cn(2n+1)/(n+2)
实现代码如下
func numTrees(n int) int {
s := 1
for i:=0; i<n; i++ {
// fmt.Println(s)
s=2*s*(2*i+1)/(i+2)
}
return s
}
提交结果如下,内存使用降低了一点,但时间上由于n最大仅为19,所以时间效率提升并不明显