95. 不同的二叉搜索树 II、96. 不同的二叉搜索树
95
Tg:递归
这题不能算DP吧,就是递归
一个问题:每次的树都要新建,不能共用一个根节点,否则下次遍历对根左右子树的改动会把已经放进结果数组中的树改掉。。
class Solution:
def generateTrees(self, n: int):
def func(m,n):
#[m,n]闭区间
res=[] if m<=n else[None]
for i in range(m,n+1):
print(i)
left_tree=func(m,i-1)
right_tree=func(i+1,n)
for le in left_tree:
for ri in right_tree:
#必须在这里新建节点,因为每次循环生成的树
#都不一样,不能共用根节点,这样会导致res之前导入
#的树因为之后的遍历发生改动
root=TreeNode(i)
root.left=le
root.right=ri
res.append(root)
return res
return func(1,n) if n else []
96
Tg:Dp
这题和上题类似,但上题要给出所有树的解集,而本题只需要算树的个数,如果直接把上一题代码搬过来加个计数的话会超时,因为1.不需要真的去建立节点和树,只需要算数。2.举例对[1,4]和[5,8]建树,在上一题中这两者完全是不同的,因为树结构不一样,是两棵不同的树。但这题中这两段序列分别建树的个数是一样的,即在96中只要区间长度一样,建立树的数量就一样。
反例(我一开始写的),没用DP。还是按照上一题的思路,对同样区间长度的不同区间分开处理的,加入字典。虽然省略了建树的过程,但还是浪费了时间。
class Solution:
def numTrees(self, n: int) -> int:
dic={}
def func(m,n):
res=0 #当前[m,n]闭区间树的数量
if m>=n:
return 1 #None节点也算1个
for i in range(m,n+1):
#i做根
left=dic[(m,i-1)]if (m,i-1) in dic else func(m,i-1)
right=dic[(i+1,n)]if (i+1,n) in dic else func(i+1,n)
res+=left*right
dic[(m,n)]=res
return res
return func(1,n) if n else 0
官方题解,完全的DP。G(n)为长度n的区间的树数量,外层循环区间长度i为2到n,内层循环根的位置1到i,左子树的G值乘右子树的G值就是当前根情况下的树数量。
class Solution:
def numTrees(self, n):
"""
:type n: int
:rtype: int
"""
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]
C++的,建立map,对于长度相同的序列只算一次。
class Solution {
public:
int numTrees(int n) {
if(n==0){
return 0;
}
map<int,int> dic;
dic[0]=dic[1]=1;
func(n,dic);
return dic[n];
}
void func(int n,map<int,int>& dic){
if(dic[n]!=0){
return;
}
for(int i=1;i<=n;++i){
if(dic[i-1]==0){
func(i-1,dic);
}
if(dic[n-i]==0){
func(n-i,dic);
}
dic[n]+=dic[i-1]*dic[n-i];
}
}
};
进击的小🐴农