LeetCode 530.二叉搜索树的最小绝对差:
思路:
类似前面验证二叉树的题目。中序遍历二叉搜索树得到的为有序数组。
- 中序遍历二叉搜索树,数组记录其中序序列,最后在有序数组上求最小差值
- 在遍历过程求最小差值,使用pre指向遍历的前一个结点
下面代码为方法2
# 递归 + 递归中更新最小值
class Solution:
def getMinimumDifference(self, root: Optional[TreeNode]) -> int:
self.minNum = float('inf')
self.pre = None
self.traversal(root)
return self.minNum
def traversal(self, node):
if not node:
return
self.traversal(node.left)
if self.pre:
self.minNum = min(self.minNum, node.val - self.pre.val)
self.pre = node
self.traversal(node.right)
# 递归 + 函数返回二叉搜索树中的最小差值
class Solution:
def getMinimumDifference(self, root: Optional[TreeNode]) -> int:
self.pre = None
minNum = self.traversal(root)
return minNum
def traversal(self, node):
if not node:
return float('inf')
minNum = self.traversal(node.left) # 左
if self.pre and node.val - self.pre.val < minNum: # 中
minNum = node.val - self.pre.val
self.pre = node # 更新self.pre
rightMin = self.traversal(node.right) # 右
if minNum > rightMin:
minNum = rightMin
return minNum
# 迭代
class Solution:
def getMinimumDifference(self, root: Optional[TreeNode]) -> int:
miniNum = float('inf')
stack = []
cur, pre = root, None
while cur or stack:
if cur:
stack.append(cur)
cur = cur.left
else:
cur = stack.pop()
# 二叉搜索树的中序遍历是有序数组
if pre and cur.val - pre.val < miniNum:
miniNum = cur.val - pre.val
pre = cur
cur = cur.right
return miniNum
LeetCode 501.二叉搜索树中的众数:
思路:
- 根据二叉搜索树的特征
中序遍历为有序数组
- 1.1 中序遍历得到数组,再在数组上进行判断。
# 此处循环过程中传入的count为当前结点的count
class Solution:
def findMode(self, root: Optional[TreeNode]) -> List[int]:
self.vec = []
self.traversal(root)
len_vec = len(self.vec)
result = []
maxCount, count = 0, 1
for i in range(len_vec):
if (i < len_vec - 1 and self.vec[i] != self.vec[i + 1]) or i == len_vec - 1:
if count > maxCount:
maxCount = count
result = [self.vec[i]]
elif count == maxCount:
result.append(self.vec[i])
count = 1 # 更新count为下一个结点的count
else:
count += 1 # 更新count为下一个结点的count
return result
def traversal(self, node):
if not node:
return
self.traversal(node.left)
self.vec.append(node.val)
self.traversal(node.right)
- 1.2 使用双指针,pre指向之前访问的结点,此处传入的count为pre结点的count,先更新count为当前结点的,再对result进行处理
# 递归
class Solution:
def printLits(self, plist):
for num in plist:
print(num)
print()
def findMode(self, root: Optional[TreeNode]) -> List[int]:
self.pre = None
self.result = []
self.count, self.maxCount = 0, 0
self.traversal(root)
return self.result
def traversal(self, node):
if not node:
return
self.traversal(node.left)
# 更新self.count为当前node.val的个数
if self.pre and self.pre.val == node.val:
self.count += 1
elif self.pre and self.pre.val != node.val:
self.count = 1
else: # 第一个遍历的结点
self.count = 1
self.pre = node # 容易忘记
# 是否更新result
if self.count > self.maxCount:
self.maxCount = self.count
self.result = [node.val]
elif self.count == self.maxCount:
self.result.append(node.val)
self.traversal(node.right)
#迭代
class Solution:
def findMode(self, root: Optional[TreeNode]) -> List[int]:
pre = None
maxCount, count = 0, 0
result = []
stack = []
cur = root
while cur or stack:
if cur:
stack.append(cur)
cur = cur.left
else:
cur = stack.pop()
# 更新count
if pre and pre.val == cur.val:
count += 1
else:
count = 1
# 更新pre
pre = cur
# 更新result
if count > maxCount:
maxCount = count
result = [cur.val]
elif count == maxCount:
result.append(cur.val)
# 访问下一个结点
cur = cur.right
return result
- 针对一般二叉树的方法
遍历二叉树,统计value和对应的频率为map,反转频率为key,值为value,最后对key进行排序
from collections import defaultdict
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def findMode(self, root: Optional[TreeNode]) -> List[int]:
numFreMap = {}
stack = [root]
# 遍历得到map
while stack:
cur = stack.pop()
numFreMap[cur.val] = numFreMap.get(cur.val, 0) + 1
if cur.right: stack.append(cur.right)
if cur.left: stack.append(cur.left)
# 字典的键值翻转
freNumMap = defaultdict(list)
for value, fre in numFreMap.items():
freNumMap[fre].append(value)
# 排序频率
frequencies = list(freNumMap.keys())
frequencies.sort(reverse=True)
return freNumMap[frequencies[0]]
感悟:
- 使用双指针pre和cur对二叉搜索树进行中序遍历时,如果对结点的处理比较复杂,pre的更新容易忘记
- 计数count需要清楚传入的count是cur结点的,还是pre结点的;如果是当前结点的,对当前结点进行处理后,下一次循环前要更新count;如果是pre结点的,需要先处理count为cur结点的,再对当前结点进行处理
学习时间:
提示:这里可以添加计划学习的时间
例如:
- 周一至周五晚上 7 点—晚上9点
- 周六上午 9 点-上午 11 点
- 周日下午 3 点-下午 6 点
LeetCode 236.二叉树的最近公共祖先:
思路:
使用回溯和后序遍历完成从底向上搜索的过程
最近公共祖先一共有两种情况
1)如下图,p,q分别在其左右子树
2)p / q其中一个就是公共祖先
- 使用回溯保存路径
使用回溯得到根节点到p / q的路径。
① 参数和返回值:传入node 和路径path(记录的是包含了当前节点的路径),因为有全局变量result保存目标结点的路径,不需要返回值
② 边界条件:空结点,当前结点为p or q,处理之后返回(对于情况1,p和q的路径都会被记录,对于情况2,只需要记录其中一个节点的路径,因为那个节点就是最近公共祖先)
③ 正常递归:处理左右子树,加剪枝
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
self.p, self.q = p, q
self.result = []
self.traversal(root, [root])
if len(self.result) == 1:
return self.result[0][-2]
path1, path2 = self.result[0], self.result[1]
i = 0
while path1[i] and path2[i] and path1[i] == path2[i]:
i += 1
return path1[i - 1]
#return []
def traversal(self, node, path):
#self.printList(path)
if node == self.p or node == self.q:
#print("find")
#print()
path.append(None)
self.result.append(path[:])
path.pop()
return
if not node.left and not node.right:
return
if len(self.result) == 2: return # 剪枝
if node.left: self.traversal(node.left, path + [node.left]) #隐形回溯
if node.right: self.traversal(node.right, path + [node.right])
def printList(self, plist):
for node in plist:
print(node.val)
print()
- 使用回溯返回最近公共节点
采用后序遍历且需要返回最近公共节点,需要将整个二叉树遍历完成
① 参数和返回值:传入node,返回节点
② 边界条件:空结点,当前节点为p,或q,直接返回
③ 正常递归:首先递归左右子树,且保存返回值。此时当前节点有三种情况,
1)左右子树返回值都不为空,当前节点为最近公共子节点,返回当前节点
2)左右子树一个返回值为空,另一个不为空,当前节点为最近公共子节点的祖先节点,返回子树的返回值
3)左右子树返回值均为空,当前节点与最近公共子节点无关,返回None
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
self.p, self.q = p, q
return self.traversal(root)
def traversal(self, node):
if not node or node == self.p or node == self.q:
return node
# 后序遍历
leftN = self.traversal(node.left)
rightN = self.traversal(node.right)
if leftN and rightN:
return node
elif leftN and not rightN:
return leftN
elif not leftN and rightN:
return rightN
else:
return None
感悟:
回溯参数包含path时,要明确path是否包含当前节点node,换句话说,就是传入参数path是当前节点的,还是前一个结点的;需要在结点处理前更新还是结点处理后更新。
最近公共子节点还可以研究一下