什么事二叉搜索数
leetcode98. 验证二叉搜索树
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:
2
/
1 3
输出: true
示例 2:
输入:
5
/
1 4
/
3 6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。
这道题么考频不是很高,但诠释了二叉搜索树的概念,掌握起来也是很快的,是二叉搜索树的基础题
做完了就知道二叉搜索树大概是个什么东西
python通过代码
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
return self.helper(root,None,None)
def helper(self,root,low,high):
if not root: return True
if low != None and root.val <= low: return False
if high != None and root.val >= high: return False
if not self.helper(root.left,low,root.val): return False
if not self.helper(root.right,root.val,high): return False
return True
规模为n的二叉搜索树一共有多少种?
leetcode96. 不同的二叉搜索树
95,96是二叉树标签下考频最高的两道题,尤其重要,必须牢固掌握
给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?
示例:
输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:
1 3 3 2 1
/ / /
3 2 1 1 3 2
/ /
2 1 2 3
这道题用dp去做,
状态转移方程看东北程序员视频里的推导
这个递推式有必要记下来,因为这是卡特兰数的递推式
在leetcode中碰到的组合题,前几项n=1,2,3,4的时候 结果数列为1,2,5,14,...
看到1,2,5,14就要敏感,这个东西基本就是卡特兰数了,因为他不会给你太弱智的找规律
那知道这个是卡特兰数之后,问题就迎刃而解了
class Solution:
def numTrees(self, n: int) -> int:
dp = [1] * (n+1)
for i in range(1,n+1):
sum_ = 0
for j in range(i):
sum_ += dp[j]*dp[i-j-1]
dp[i] = sum_
return dp[-1]
总结 卡特兰数记两个东西
1.通项公式(也就是题目dp中的状态转移方程)
2.前四项1,2,5,14
周赛如果遇到了,直接编,想都不要想,碰运气,编不出来拉倒
如果不放心,基本也可以顺着卡特兰数通项的思路去想前后两个数之间的关系,逻辑理顺了就能确保是卡特兰数了
95. 不同的二叉搜索树 II
这道题比96更难一点,相比96他要生成所有的二叉树,但可以按96如法炮制,用很复杂的dp去做(嵌套了四层for循环),实际上时间么在可接受范围之内,卡的不是很严
先看题目吧
给定一个整数 n,生成所有由 1 ... n 为节点所组成的二叉搜索树。
示例:
输入: 3
输出:
[
[1,null,3,2],
[3,2,null,1],
[3,1,null,null,2],
[2,1,3],
[1,null,2,null,3]
]
解释:
以上的输出对应以下 5 种不同结构的二叉搜索树:
1 3 3 2 1
/ / /
3 2 1 1 3 2
/ /
2 1 2 3
如果刚做完96再来做95 思路是很明晰的
class Solution:
def generateTrees(self, n: int) -> List[TreeNode]:
dp = [[None]] * (n+1)
if not n: return []
for i in range(1,n+1):
dp[i] = []
for j in range(i):
for lefttree in dp[j]:
for righttree in dp[i-j-1]:
thistree = TreeNode(j+1)
thistree.left = lefttree
thistree.right = self.copytree(righttree,j+1)
dp[i].append(thistree)
return dp[-1]
def copytree(self,root,k):
if not root: return None
res = TreeNode(root.val+k)
res.left = self.copytree(root.left,k)
res.right = self.copytree(root.right,k)
return res
需要注意的点是,左子树可以直接引用传参,但右子树需要给每个节点加上数值
比如1234567 取j=3
你把4作为root,左子树递归调用dp[3]
右子树也调用dp[3],但出来的子树是123,而你需要的右子树范围是567
所以这里每个节点的数值加上j+1(3+1=4)进行深拷贝
额外实现了一个深拷贝+更改节点数值的函数在下面
这个copy函数很简单了,二叉树遍历法的常规操作
这道题不止这一个方法,还有一个方法请看东北程序员
https://www.bilibili.com/video/av68783564?p=93
第二种写法就是DFS+递归,也许面试官问到更想听这种方法,比第一种代码简洁很多
class Solution:
def generateTrees(self, n: int) -> List[TreeNode]:
if not n: return []
return self.helper(1,n)
def helper(self,i,j):
if j < i: return [None]
res = []
for idx in range(i,j+1):
left = self.helper(i,idx-1)
right = self.helper(idx+1,j)
for lefttree in left:
for righttree in right:
thistree = TreeNode(idx)
thistree.left = lefttree
thistree.right = righttree
res.append(thistree)
return res
一个坑:这里我建议把helper的i,j换成start,end或者left,right. 因为我一开始把其中一个i打成1了,报错找了很久才找到,由此可见命名规范的重要性
时间也比上一种少
执行结果:
通过
显示详情
执行用时 :52 ms, 在所有 python3 提交中击败了96.03% 的用户
内存消耗 :14 MB, 在所有 python3 提交中击败了95.19%的用户
99. 恢复二叉搜索树
二叉搜索树中的两个节点被错误地交换。
请在不改变其结构的情况下,恢复这棵树。
示例 1:
输入: [1,3,null,null,2]
1
/
3
2
输出: [3,1,null,null,2]
3
/
1
2
示例 2:
输入: [3,1,4,null,null,2]
3
/
1 4
/
2
输出: [2,1,4,null,null,3]
2
/
1 4
/
3
进阶:
使用 O(n) 空间复杂度的解法很容易实现。
你能想出一个只使用常数空间的解决方案吗?
其实就遍历一遍,用全局变量记录,因为函数递归调用起来记录变量顺序很麻烦,必须用全局变量脱离递归套用的约束,省去传参的麻烦
class Solution:
def recoverTree(self, root: TreeNode) -> None:
"""
Do not return anything, modify root in-place instead.
"""
self.err1 = None
self.err2 = None
self.pre = None
def helper(root):
if not root: return
helper(root.left)
if self.pre and self.pre.val > root.val and not self.err1:
self.err1 = self.pre
if self.err1 and self.pre.val > root.val:
self.err2 = root
self.pre = root
helper(root.right)
helper(root)
self.err1.val,self.err2.val = self.err2.val,self.err1.val
这里python用的是类的属性变量来模拟全局变量(因为函数内部也可以直接作用类的属性变量)
凡是可以递归的东西必然可以迭代,而这题使用迭代就不需要全局变量的概念了