530.二叉搜索树的最小绝对差
题目链接:二叉搜索树的最小绝对差
解法:
注意是二叉搜索树,二叉搜索树可是有序的。遇到在二叉搜索树上求什么最值啊,差值之类的,就把它想成在一个有序数组上求最值,求差值。
其实就是做一个中序遍历,得到一个递增的有序数组,然后从数组的左边遍历到数组右边即可。
中序遍历用递归和迭代法都可以。
要注意的是任意两点的差值,而不是节点和左右子节点的差值。
递归法还有一种双指针做法,直接在中序遍历的过程中,就把最小差值计算出来了。有点不好理解,但是值得学习这种思路。
边界条件:无
时间复杂度:O(logn)
空间复杂度:O(logn)
# 深度优先搜索:递归法,中序遍历
# 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 getMinimumDifference(self, root):
"""
:type root: TreeNode
:rtype: int
"""
vals = self.traversal(root)
min_diff = float("inf")
# 前后两个元素相减,再比较得到最小差值
for i in range(1, len(vals)):
min_diff = min(min_diff, vals[i] - vals[i-1])
return min_diff
def traversal(self, root):
# 使用中序遍历,得到一个递增的有序数组
if not root:
return []
left = self.traversal(root.left)
right = self.traversal(root.right)
return left + [root.val] + right
# 深度优先搜索:递归法,中序遍历,双指针
# 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 getMinimumDifference(self, root):
"""
:type root: TreeNode
:rtype: int
"""
self.pre = None
self.result = float("inf")
self.traversal(root)
return self.result
def traversal(self, cur):
if not cur:
return
self.traversal(cur.left)
if self.pre:
self.result = min(self.result, cur.val - self.pre.val)
# 中序遍历,由于是递增的,所以把前一个node记录为pre
self.pre = cur
self.traversal(cur.right)
# 深度优先搜索:迭代法加双指针
# 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 getMinimumDifference(self, root):
"""
:type root: TreeNode
:rtype: int
"""
stack = []
cur = root
result = float("inf")
pre = None
while cur or stack:
if cur:
stack.append(cur)
cur = cur.left
else:
cur = stack.pop()
if pre:
result = min(result, cur.val - pre.val)
pre = cur
cur = cur.right
return result
501.二叉搜索树中的众数
题目链接:二叉搜索树中的众数
解法:
常规方法:如果不是二叉搜索树,最直观的方法一定是把这个树都遍历了,用map统计频率,把频率排个序,最后取前面高频的元素的集合。实现的时候,在遍历时就统计元素的个数,而不是先得到递增数组,再统计个数,这样可以减少一次遍历。
利用二叉搜索树的特性:
如果要求不使用额外空间,那么就不能用map来统计频率,这就需要用到pre指针和cur指针的技巧。利用二叉搜索树的特性,使用中序遍历,pre指针的值小于或等于cur指针的值,那么可以不断更新最新的最大频率和结果列表。如果 频率count 等于 maxCount(最大频率),当然要把这个元素加入到结果集中。频率count 大于 maxCount的时候,不仅要更新maxCount,而且要清空结果集,再添加新的结果。
递归法和迭代法,上述的处理逻辑和代码相同。
边界条件:无
时间复杂度:O(n)。即遍历这棵树的复杂度。
空间复杂度: 递归为O(n),迭代为O(1)
# 递归法:使用额外空间(hashmap),只遍历两遍
# 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 findMode(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
# 统计数组中每个数的个数
self.count = {}
vals = self.traversal(root)
# 计算最大个数,并取出对应的值
max_count = max(self.count.values())
return [k for k, v in self.count.items() if v == max_count]
def traversal(self, root):
# 中序遍历,得到递增的数组
if not root:
return
self.traversal(root.left)
self.count[root.val] = self.count.get(root.val, 0) + 1
self.traversal(root.right)
# 递归法:不使用额外空间(hashmap),只进行两次遍历
# 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 findMode(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
self.max_count = float("-inf")
self.count = 0
self.result = []
self.pre = None
self.traversal(root)
return self.result
def traversal(self, cur):
# 中序遍历,得到递增的数组
if not cur:
return
self.traversal(cur.left)
# 如果是第一个节点
if not self.pre:
self.count =1
# 如果不是第一个节点,且和前一个节点值相同
elif self.pre.val == cur.val:
self.count += 1
# 如果值不同
else:
self.count =1
# 如果和最大值相等,则添加到结果集中
if self.count == self.max_count:
self.result.append(cur.val)
elif self.count > self.max_count:
self.max_count = self.count
self.result = [cur.val]
# 更新pre指针
self.pre = cur
self.traversal(cur.right)
# 迭代法:不使用额外空间(hashmap)
# 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 findMode(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
max_count = 0
count = 0
result = []
pre = None
stack = []
cur = root
while cur or stack:
if cur:
stack.append(cur)
cur = cur.left
else:
cur = stack.pop()
# 如果是第一个节点
if not pre:
count = 1
# 如果不是第一个节点,且和前一个节点值相同
elif pre.val == cur.val:
count += 1
# 如果值不同
else:
count = 1
# 如果和最大值相等,则添加到结果集中
if count == max_count:
result.append(cur.val)
elif count > max_count:
max_count = count
result = [cur.val]
# 更新pre指针
pre = cur
cur = cur.right
return result
236. 二叉树的最近公共祖先
题目链接:二叉树的最近公共祖先
解法:
整体的思路是使用后序遍历和回溯。二叉树回溯的过程就是从底到上。后序遍历(左右中)就是天然的回溯过程,可以根据左右子树的返回值,来处理中节点的逻辑。
第一个情况:如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。
判断逻辑是 如果递归遍历遇到q,就将q返回,遇到p 就将p返回,那么如果 左右子树的返回值都不为空,说明此时的中节点,一定是q 和p 的最近祖先。
在回溯的过程中,必然要遍历整棵二叉树,即使已经找到结果了,依然要把其他节点遍历完,因为要使用递归函数的返回值(也就是代码中的left和right)做逻辑判断。
第二个情况:节点本身p(q),它拥有一个子孙节点q(p)。遇到 q 或者 p 就返回,这样也包含了 q 或者 p 本身就是 公共祖先的情况。一个关键问题是,如果在左子树遇到了q或者p,而右子树没有p或者q,就返回左子树的q或者q,那么如果p或者q就在它的下一层的左右子树上,就不去查找了吗?不需要了,因为q和p一定存在。而且先查找到的,一定是位置靠上的,所以直接返回就行。
这个题目写起来真的有点难度,主要回溯确实有点难理解。
这个题不适合用迭代法。
边界条件:无
时间复杂度:O(N),其中 N是二叉树的节点数。二叉树的所有节点有且只会被访问一次,因此时间复杂度为 O(N)。
空间复杂度:O(N) ,其中 N是二叉树的节点数。递归调用的栈深度取决于二叉树的高度,二叉树最坏情况下为一条链,此时高度为 N,因此空间复杂度为 O(N)。
# 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):
return self.traversal(root, p, q)
def traversal(self, root, p, q):
if not root:
return None
if root == p or root == q:
return root
# 从下往上处理,需要用到后序遍历
left = self.traversal(root.left, p, q)
right = self.traversal(root.right, p, q)
if (left and right):
return root
elif left and not right:
return left
elif right and not left:
return right
else:
return None