Leetcode 98 验证二叉搜索树
题目:
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:root = [2,1,3]
输出:true
示例 2:
输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。
提示:
- 树中节点数目范围在[1, 1 0 4 10^4 104] 内
- - 2 31 2^{31} 231 <= Node.val <= 2 31 2^{31} 231 - 1
前言:
什么是二叉搜索树?简单说:根节点大于左子树上的任何值;小于右子树上的任何值。子树同样满足这样的定义。
算法思想:
- 递归法:
(1)利用双指针法:
递归参数:二叉树根节点
递归结束条件:当当前节点位None,返回;但无返回值,意思是结束当前递归,返回到上一层。
注: python中return的作用:- 返回值(多个值)
- 结束当前函数
一次递归逻辑:首先是左递归;中序处理pre(cur的前一个节点)的value 是否严格小于cur(当前节点)的value;递归遍历右子树;
(2)定义最小值法:
思路基本与(1)相似,按照二叉排序树的性质,中序遍历的节点是有序的,也就是递增的。也就是用maxValue记录cur节点前的节点的value,即遍历到cur之前节点的最大值。如果cur.value>maxValue,则更新maxValue;否则返回False,即不满足条件。
2. 迭代法:
依然采用中序遍历,在“中序”处理逻辑处类似递归法。
代码实现:
- 递归法:
(1)双指针法:
# 定义树节点
class TreeNode:
def __init__(self, val=0, left=None, right=None) -> None:
self.val = val
self.left = left
self.right = right
class Solution:
# 定义全局指针,指向当前节点的前一个节点
# 此后双指针法遍历二叉树,判断是否是二叉搜索树
def __init__(self) -> None:
self.pre = None
def isValidBST(self, root):
if root is None:
return True
left = self.isValidBST(root.left)
# self.pre is not None: 意味着递归到最后一个节点不用和root.val比较
# self.pre.val >= root.val意味着当前“双指针”开始移动了
if self.pre is not None and self.pre.val >= root.val:
return False
self.pre = root
right = self.isValidBST(root.right)
return left and right
(2)maxValue方式:
# 定义树结构
class TreeNode:
def __init__(self, val=0, left=None, right=None) -> None:
self.val = val
self.left = left
self.right = right
class Solution:
def __init__(self) -> None:
self.maxValue = float('-inf')
def isValidBST(self, root):
if root is None:
return True
left = self.isValidBST(root.left)
if root.val > maxValue:
maxValue = root.val
else:
return False
right = self.isValidBST(root.right)
return left and right
- 迭代法:
# 定义树结构
class TreeNode:
def __init__(self, val=0, left=None, right=None) -> None:
self.val = val
self.left = left
self.right = right
# 利用中序遍历的思想
class Solution:
def isValidBST(self, root):
if root is None:
return True
stack = []
pre = None
cur = root
while cur or stack:
# 一路向左
if cur:
stack.append(cur)
cur = cur.left
else:
cur = stack.pop()
if pre is not None and cur.val <= pre.val:
return False
pre = cur
cur = cur.right
return True
Leetcode 530 二叉搜索树的最小绝对差
题目:
给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。
差值是一个正数,其数值等于两值之差的绝对值。
示例 1:
输入:root = [4,2,6,1,3]
输出:1
示例 2:
输入:root = [1,0,48,null,null,12,49]
输出:1
提示:
- 树中节点的数目范围是 [2, 1 0 4 10^4 104]
- 0 <= Node.val <= 1 0 5 10^5 105
算法思路:
一个常规的想法是,开辟一个数组,中序遍历二叉树(为什么中序?利用二叉搜索树的性质)将二叉树的节点都存放在数组中,然后对数组排序。这样做可以,但是有没有更好的办法呢?
- 递归法:
采用双指针法实现。前文说到,二叉搜索树具备良好的性质,因此,中序遍历二叉树是一种很好的选择。
pre指针:代表当前节点的前一个节点
cur指针:代表当前节点
递归参数:当前二叉树节点
递归结束条件:如果当前节点为None,则返回。
一次递归逻辑:按照中序遍历的思想,首先遍历左子树。如果pre 不为None,说明要将pre的值和cur的值作差(在绝对值),然后更新最小值。
最后递归遍历右子树。 - 迭代法:
按照树的中序遍历算法改动即可,逻辑参照递归法。
代码实现:
- 递归法:
# 定义树节点
class TreeNode:
def __init__(self, val=0, left=None, right=None) -> None:
self.val = val
self.left = left
self.right = right
class Solution:
# 初始化全局最小值和指针
# 笔者最开始出现的错误是每次递归都定义一个最小值和指针。
def __init__(self) -> None:
self.min_value = float('inf')
self.pre = None
def traversal(self, cur):
# 不要求有返回值,但是要求返回到上一层函数
if cur is None:
return
self.traversal(cur.left)
if self.pre is not None:
self.min_value = min(self.min_value, (cur.val - self.pre.val))
self.pre = cur
self.traversal(cur.right)
return self.min_value
def getMinimumDifference(self, root):
return self.traversal(root)
- 迭代法:
# 定义树节点
class TreeNode:
def __init__(self, val=0, left=None, right=None) -> None:
self.val = val
self.left = left
self.right = right
class Solution:
def getMinimumDifference(self, root) -> int:
min_value = float('inf')
stack = []
cur = root
pre = None
while cur or stack:
# 一路向左
if cur:
stack.append(cur)
cur = cur.left
# 直到左节点位空,出栈栈顶元素。也就是返回到上一层节点
else:
cur = stack.pop()
if pre is not None:
min_value = min(min_value, (cur.val - pre.val))
pre = cur
stack.append(cur.right)
return min_value
Leetcode 501 二叉搜索树中的众数
题目:
给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。
如果树中有不止一个众数,可以按 任意顺序 返回。
假定 BST 满足如下定义:
结点左子树中所含节点的值 小于等于 当前节点的值
结点右子树中所含节点的值 大于等于 当前节点的值
左子树和右子树都是二叉搜索树
示例 1:
输入:root = [1,null,2,2]
输出:[2]
示例 2:
输入:root = [0]
输出:[0]
提示:
- 树中节点的数目在范围 [1, 1 0 4 10^4 104] 内
- - 1 0 5 10^5 105 <= Node.val <= 1 0 5 10^5 105
算法思想:
- 递归法:
(1)利用python 内置defaultdict
defaultdict:key是node.value;value为每个value出现的次数;
递归参数:当前二叉树节点 root
递归结束条件:当当前数组没有元素时,说明遍历到空节点,返回即结束当前函数
一次递归逻辑:正常中序递归
(2)普通计数思想,双指针法
递归参数和递归结束条件相同
pre:中序遍历cur节点的前一个节点;cur:当前节点;
一次递归逻辑:左递归;中序处理;右递归;其中中序处理重点说,如何表示当前是第一个节点?pre为空,cur指向第一个节点,也就是最左侧节点,此时count=1;接下来,如果pre.value = cur.value,说明cur当前指向的值与pre.value相等,count++;否则pre.value != cur.value,说明遇到了一个新的值,count=1。 - 迭代法:
采用中序遍历思想,逻辑与递归法相同,采用双指针法。
代码实现:
- 递归法:
(1)内置defaultdict:
# 定义树节点
from collections import defaultdict
class TreeNode:
def __init__(self, val=0, left=None, right=None) -> None:
self.val = val
self.left = left
self.rigth = right
class Solution:
def searchBST(self, cur, freq_map):
if cur is None:
return
freq_map[cur.val] += 1
self.searchBST(cur.left, freq_map)
self.searchBST(cur.right, freq_map)
def findMode(self, root):
freq_map = defaultdict(int)
result = []
if root is None:
return result
self.searchBST(root, freq_map)
max_freq = max(freq_map.values())
for key, freq in freq_map.items():
if freq == max_freq:
result.append(key)
return result
(2)双指针法:
# 定义树节点
class TreeNode:
def __init__(self, val=0, left=None, right=None) -> None:
self.val = val
self.left = left
self.right = right
class Solution:
def __init__(self) -> None:
self.maxCount = 0
self.count = 0
self.pre = None
self.result = []
def searchBST(self, cur):
if cur is None:
return
self.searchBST(cur.left)
if self.pre is None:
self.count += 1
elif self.pre.val == cur.val:
self.count += 1
else:
self.count = 1
self.pre = cur
if self.count == self.maxCount:
self.result.append(cur.val)
if self.count > self.maxCount:
self.maxCount = self.count
self.result = [cur.val]
self.searchBST(cur.right)
return
def findMode(self, root):
self.maxCount = 0
self.count = 0
self.pre = None
self.result = []
self.searchBST(root)
return self.result
- 迭代法:
# 定义树节点
class TreeNode:
def __init__(self, val=0, left=None, right=None) -> None:
self.val = val
self.left = left
self.right = right
class Solution:
def findMode(self, root):
stack = []
result = []
pre = None
maxCount = 0
count = 0
cur = root
while cur or stack:
if cur:
stack.append(cur)
cur = cur.left
else:
cur = stack.pop()
if pre is None: # 处理第一个节点
count = 1
elif cur.val == pre.val: # 与前一个节点的值相同
count += 1
else:
count = 1 # 与前一个节点的值不同,重新置为1
pre = cur
if count == maxCount:
result.append(cur.val)
if count > maxCount:
maxCount = count
result = [cur.val]
pre = cur
cur = cur.right
return result