代码随想录算法训练营第21天 | 530.二叉搜索树的最小绝对差、501.二叉搜索树中的众数、236. 二叉树的最近公共祖先

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  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值