530.二叉搜索树的最小绝对差
本题和 98.验证二叉搜索树 的逻辑类似,由于二叉搜索树的特性,中序遍历的结果将是单调递增的,最小绝对差一定发生在遍历的相邻节点。我们可以遍历完后,再对得到的中序数组进行处理,或者在遍历时直接进行处理。
思路1:中序遍历二叉搜索树,得到一个单调递增的数组,接着从中寻找最小绝对差。
class Solution:
def getMinimumDifference(self, root: Optional[TreeNode]) -> int:
# 中序遍历
def inorderTraversal(node):
if not node:
return []
left = inorderTraversal(node.left)
right = inorderTraversal(node.right)
return left + [node.val] + right
inorder = inorderTraversal(root)
min_diff = float('inf')
for i in range(1, len(inorder)):
min_diff = min(min_diff, inorder[i]-inorder[i-1])
return min_diff
思路2:中序遍历的过程中,计算相邻遍历节点的绝对差,维护一个最小绝对差。我们可以定义一个指针 prev,指向当前节点的前一节点。
class Solution:
def getMinimumDifference(self, root: Optional[TreeNode]) -> int:
# prev在被更新为第一个遍历节点值之前,
# 会先用来被计算一次 min_diff(此并非树中任何节点之间的差值,因为prev此时并不是某一节点)
# 为了确真正的节点差值能被正确记录,初始化prev为一个很小的负数
# 这样 min_diff 就会被初始化为一个一个很大的数值
self.prev = -float('inf')
self.min_diff = float('inf')
def inorderTraversal(node):
if not node:
return
inorderTraversal(node.left)
self.min_diff = min(self.min_diff, node.val - self.prev)
self.prev = node.val
inorderTraversal(node.right)
inorderTraversal(root)
return self.min_diff
501.二叉搜索树中的众数
暴力:遍历二叉树,使用哈希表记录元素出现频率,按照频率排序,输出元素。
class Solution:
def findMode(self, root: Optional[TreeNode]) -> List[int]:
freq_map = defaultdict(int)
modes = []
def postorder(node, freq_map):
if not node:
return
freq_map[node.val] += 1
postorder(node.left, freq_map)
postorder(node.right, freq_map)
# 遍历二叉树,填充字典
postorder(root, freq_map)
# 排序字典
max_freq = max(freq_map.values())
# 收集出现频率最高的数字
modes = [k for k, v in freq_map.items() if v == max_freq]
return modes
思路:利用二叉搜索树的特性,同样是在遍历过程中统计每个元素出现的频率,并维护一个最大频率。由于我们需要完整遍历整个树才能确切知道最大频率,因此这里采取这样的措施:如果当前记录的最大频率被更新,则清空已收集的元素,重新收集。
这个过程可以分为以下几步:
- 如果当前节点值等于前一个节点值(
prev
),currCount
加一,因为这意味着当前值与之前的值相同,我们正在计算这个特定值的出现次数。- 如果当前节点值不等于前一个节点值,这意味着我们开始计算一个新值的出现次数,所以需要将
currCount
重置为1。- 每次更新
currCount
后,比较它与maxCount
。根据这个比较的结果:
- 如果
currCount
大于maxCount
,这意味着我们找到了一个新的频率更高的值。因此,需要更新maxCount
为新的currCount
,清空众数列表,并将当前节点值加入众数列表。- 如果
currCount
等于maxCount
,这意味着当前节点值的出现频率与已知的最高频率相同,所以需要将当前节点值加入到众数列表中,但不需要清空原有的众数列表。- 如果
currCount
小于maxCount
,则不需要对maxCount
或众数列表做任何更改。
class Solution:
def findMode(self, root: Optional[TreeNode]) -> List[int]:
self.currCount = 0
self.maxCount = 0
self.prev = None
self.modes = []
def inorder(node):
if not node:
return
# 左
inorder(node.left)
# 中
if node.val == self.prev:
self.currCount += 1
else:
self.currCount = 1
self.prev = node.val
if self.currCount > self.maxCount:
self.maxCount = self.currCount
self.modes = [node.val]
elif self.currCount == self.maxCount:
self.modes.append(node.val)
# 右
inorder(node.right)
inorder(root)
return self.modes
236. 二叉树的最近公共祖先
本题需要判断左右子树的情况,才能确定此节点是否为公共祖先。因此采用后序遍历。
思路是从根节点开始,递归地在左右子树中查找指定的两个节点。对于每个节点来说,可能存在以下几种情况:
- 如果当前节点就是两个节点中的一个,那么它就可能是最近公共祖先。
- 如果当前节点的左右子树分别包含这两个节点,那么当前节点就是它们的最近公共祖先。
- 如果这两个节点都位于当前节点的同一侧(都在左子树或都在右子树),那么最近公共祖先将在那一侧的子树中。
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
# 递归终止条件
# 空节点,或者找到了p或q
if not root or root == p or root == q:
return root
# 在左子树中递归寻找p或q
left = self.lowestCommonAncestor(root.left, p, q)
# 在右子树中递归寻找p或q
right = self.lowestCommonAncestor(root.right, p, q)
# 如果左子树和右子树各自找到了p和q(即left和right都非空),
# 那么当前节点就是最近公共祖先
if left and right:
return root
# 如果左子树为空,那么p和q都在右子树中,返回右子树的结果
if not left:
return right
# 如果右子树为空,那么p和q都在左子树中,返回左子树的结果
if not right:
return left