代码随想录算法训练营第21天|二叉树part07|530. 二叉搜索树的最小绝对差、236. 二叉树的最近公共祖先
530. 二叉搜索树的最小绝对差
自己做
思路:
因为是二叉搜索树,所以我想到使用中序遍历
想着一次递归就实现求最小绝对差,但是发现根结点的前驱节点无法记录,需要使用全局的前驱指针,在递归函数里面传递pre不行
因此失败
代码:
python
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def __init__(self):
self.minDiff = 10e4
def getMinimumDifference(self, root):
"""
:type root: TreeNode
:rtype: int
"""
# 利用二叉搜索树的特性,最小差值一定出现在相邻的两个结点之间
# 中序遍历
pre = None
self.helper(pre, root)
return self.minDiff
def helper(self, pre, root):
if root == None:
return
self.helper(root, root.left)
if pre:
if abs(pre.val - root.val) < self.minDiff:
self.minDiff = abs(pre.val - root.val)
# 根结点的前驱节点如何解决,根结点的后继节点如何解决
self.helper(pre, root.right)
代码随想录
思路:
递归 + 中序 + 结合数组
代码:
python
class Solution:
def __init__(self):
self.vec = []
def traversal(self, root):
if root is None:
return
self.traversal(root.left)
self.vec.append(root.val) # 将二叉搜索树转换为有序数组
self.traversal(root.right)
def getMinimumDifference(self, root):
self.vec = []
self.traversal(root)
if len(self.vec) < 2:
return 0
result = float('inf')
for i in range(1, len(self.vec)):
# 统计有序数组的最小差值
result = min(result, self.vec[i] - self.vec[i - 1])
return result
或者采用一个全局的pre指针
class Solution:
def __init__(self):
self.result = 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.result = min(self.result, cur.val - self.pre.val)
self.pre = cur # 记录前一个
self.traversal(cur.right) # 右
def getMinimumDifference(self, root):
self.traversal(root)
return self.result
501.二叉搜索树中的众数
自己做
自己做没有做出来,原因是把计数和更新结果数组放到一起了,比较混乱,而代码随想录则分开来处理,思路清晰,不易错!
思路:
代码:
python
代码随想录
思路:
- 如果不是二叉搜索树
如果不是二叉搜索树,最直观的方法一定是把这个树都遍历了,用map统计频率,把频率排个序,最后取前面高频的元素的集合。
- 既然是搜索树,它中序遍历就是有序的。
关键是在有序数组上的话,好搞,在树上怎么搞呢?
弄一个指针指向前一个节点,这样每次cur(当前节点)才能和pre(前一个节点)作比较。
而且初始化的时候pre = NULL,这样当pre为NULL时候,我们就知道这是比较的第一个元素。
如果 频率count 等于 maxCount(最大频率),当然要把这个元素加入到结果集中(以下代码为result数组)
如果频率count 大于 maxCount的时候,不仅要更新maxCount,而且要清空结果集(以下代码为result数组)
下面的代码分两步处理:一步处理count, 一步处理结果数组
代码:
python
class Solution:
def __init__(self):
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: # 如果与最大值频率相同,放进result中
self.result.append(cur.val)
if self.count > self.maxCount: # 如果计数大于最大值频率
self.maxCount = self.count # 更新最大频率
self.result = [cur.val] # 很关键的一步,不要忘记清空result,之前result里的元素都失效了
self.searchBST(cur.right) # 右
return
def findMode(self, root):
self.count = 0
self.maxCount = 0
self.pre = None # 记录前一个节点
self.result = []
self.searchBST(root)
return self.result
236. 二叉树的最近公共祖先
自己做
思路:
代码:
python
按照代码随想录的思路自己编的
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
if root == q or root == p or root == None:
return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p , q)
if left and right:
return root
if not left and right:
return right
if left and not right:
return left
if not left and not right:
return None
代码随想录
思路:
自底向上查找
回溯啊,二叉树回溯的过程就是从低到上。
后序遍历(左右中)就是天然的回溯过程,
情况一:
首先最容易想到的一个情况:如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。
情况二:就是节点本身p(q),它拥有一个子孙节点q§。
代码实现过程都是一样的,也可以说,实现情况一的逻辑,顺便包含了情况二
递归三部曲:
-
确定递归函数返回值以及参数
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
-
确定终止条件
遇到空的话,因为树都是空了,所以返回空。
如果 root == q,或者 root == p,说明找到 q p ,则将其返回,这个返回值
处理了情况二
if (root == q || root == p || root == NULL) return root;
- 确定单层递归逻辑
本题函数有返回值,该怎么写呢?
搜索一条边的写法:
if (递归函数(root->left)) return ;
if (递归函数(root->right)) return ;
搜索整个树写法:
left = 递归函数(root->left); // 左
right = 递归函数(root->right); // 右
left与right的逻辑处理; // 中
在递归函数有返回值的情况下:如果要搜索一条边,递归函数返回值不为空的时候,立刻返回,如果搜索整个树,直接用一个变量left、right接住返回值,这个left、right后序还有逻辑处理的需要,也就是后序遍历中处理中间节点的逻辑(也是回溯)。
本题需要遍历整棵树
因为在如下代码的后序遍历中,如果想利用left和right做逻辑处理, 不能立刻返回,而是要等left与right逻辑处理完之后才能返回。
如果left 和 right都不为空,说明此时root就是最近公共节点。这个比较好理解
如果left为空,right不为空,就返回right,说明目标节点是通过right返回的,反之依然。
if (left == NULL && right != NULL) return right;
else if (left != NULL && right == NULL) return left;
else { // (left == NULL && right == NULL)
return NULL;
}
代码:
c++
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == q || root == p || root == NULL) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (left != NULL && right != NULL) return root;
if (left == NULL && right != NULL) return right;
else if (left != NULL && right == NULL) return left;
else { // (left == NULL && right == NULL)
return NULL;
}
}
};
python
class Solution:
def lowestCommonAncestor(self, root, p, q):
if root == q or root == p or root is None:
return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if left is not None and right is not None:
return root
if left is None and right is not None:
return right
elif left is not None and right is None:
return left
else:
return None