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

530.二叉搜索树的最小绝对差

思路:

注意是二叉搜索树,二叉搜索树可是有序的。

遇到在二叉搜索树上求什么最值啊,差值之类的,就把它想成在一个有序数组上求最值,求差值,这样就简单多了。

方法一:那么二叉搜索树采用中序遍历,其实就是一个有序数组。在一个有序数组上求两个数最小差值,这是不是就是一道送分题了最直观的想法,就是把二叉搜索树转换成有序数组,然后遍历一遍数组,就统计出来最小差值了。

方法二:其实在二叉搜索树中序遍历的过程中,我们就可以直接计算了。需要用一个pre节点记录一下cur节点的前一个节点。

代码:

方法一

class Solution:
    def __init__(self):
        self.vec = [] # 初始化一个空列表vec,用于存储二叉搜索树节点的值

    def getMinimumDifference(self, root: Optional[TreeNode]) -> int:
        self.vec = []
        self.traversal(root) # 调用遍历方法,将二叉搜索树的节点值按中序遍历的顺序存储到vec中
        result = float('inf') # 初始化最小差值为正无穷大
        for i in range(1, len(self.vec)): # 遍历vec列表,从第二个元素开始(即i=1)
            result = min(result, self.vec[i] - self.vec[i - 1]) # 计算当前元素与前一个元素的差值,并更新最小差值
        return result

    def traversal(self, root): # 递归函数,用于中序遍历二叉搜索树
        if root is None: # 如果当前节点为空(遇到了一个空子树),则直接返回,不再继续遍历
            return
        self.traversal(root.left) # 递归遍历左子树
        self.vec.append(root.val)  # 将当前节点的值添加到vec列表中
        self.traversal(root.right) # 递归遍历右子树

时间复杂度O(n)

空间复杂度O(n) 

方法二 

class Solution:
    def __init__(self):
        self.result = float('inf') # 初始化一个变量result,用于存储最小差值,初始化为正无穷大
        self.pre = None # 初始化一个变量pre,用于存储前一个遍历到的节点,初始化为None

    def getMinimumDifference(self, root: Optional[TreeNode]) -> int:
        self.traversal(root) # 调用traversal函数进行树的遍历,寻找最小差值
        return self.result # 返回找到的最小差值

    def traversal(self, cur): # 定义遍历函数,接受一个TreeNode类型的参数cur(代表当前遍历到的节点)
        if cur is None:# 如果当前节点为空(即已经遍历到叶子节点的下方),则直接返回 
            return
        self.traversal(cur.left) # 递归遍历左子树
        if self.pre is not None:  # 如果前一个节点pre不为空(即已经遍历过一个节点),则计算当前节点与前一个节点的差值
            self.result = min(self.result, cur.val - self.pre.val) # 更新result为当前差值与result中的较小值
        self.pre = cur  # 更新pre为当前节点
        self.traversal(cur.right)  # 递归遍历右子树

时间复杂度O(n)

空间复杂度O(n) 

501.二叉搜索树中的众数

思路:

这道题目呢,递归法我从两个维度来讲。

首先如果不是二叉搜索树的话,应该怎么解题,是二叉搜索树,又应该如何解题,两种方式做一个比较,可以加深大家对二叉树的理解。

1.如果不是二叉搜索树:如果不是二叉搜索树,最直观的方法一定是把这个树都遍历了,用map统计频率,把频率排个序,最后取前面高频的元素的集合。

2.是二叉搜索树:既然是搜索树,它中序遍历就是有序的。

遍历有序数组的元素出现频率,从头遍历,那么一定是相邻两个元素作比较,然后就把出现频率最高的元素输出就可以了。

关键是在有序数组上的话,好搞,在树上怎么搞呢?

这就考察对树的操作了。

二叉树:搜索树的最小绝对差 (opens new window)中我们就使用了pre指针和cur指针的技巧,这次又用上了。

代码:

递归法(版本一)利用字典

from collections import defaultdict

class Solution:
    def searchBST(self, cur, freq_map):
        if cur is None:
            return
        freq_map[cur.val] += 1  # 统计元素频率
        self.searchBST(cur.left, freq_map)
        self.searchBST(cur.right, freq_map)

    def findMode(self, root):
        freq_map = defaultdict(int)  # key:元素,value:出现频率
        result = []
        if root is None:
            return result
        self.searchBST(root, freq_map)
        max_freq = max(freq_map.values())
        for key, freq in freq_map.items():
            if freq == max_freq:
                result.append(key)
        return result

 时间复杂度O(n),空间复杂度O(n)


递归法(版本二)利用二叉搜索树性质 

class Solution:
    def __init__(self):
        self.maxCount = 0  # 初始化最大频率,初始值为0
        self.count = 0  # 初始化当前频率计数器,初始值为0
        self.pre = None # 初始化前一个访问的节点,初始值为None
        self.result = [] # 初始化众数列表,初始为空列表

    def findMode(self, root: Optional[TreeNode]) -> List[int]:
        # 重置频率计数器和最大频率
        self.count = 0
        self.maxCount = 0
        # 重置前一个访问的节点
        self.pre = None
        # 清空众数列表 
        self.result = []

        # 调用递归方法开始遍历BST
        self.searchBST(root)
        # 返回找到的众数列表
        return self.result

    def searchBST(self, cur):
        # 如果当前节点为空,则结束递归
        if cur is None:
            return

        # 递归遍历左子树
        self.searchBST(cur.left)
        
        # 处理当前节点
        if self.pre is None: # 如果前一个节点不存在,说明这是第一个节点
            self.count = 1 # 将当前节点值的出现次数设置为1
        elif self.pre.val == cur.val:  # 与前一个节点数值相同
            self.count += 1 # 增加计数
        else:  # 与前一个节点数值不同
            self.count = 1 # 将当前节点值的出现次数重置为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) 

时间复杂度 O(n),空间复杂度 O(n) 

236. 二叉树的最近公共祖先

思路:

遇到这个题目首先想的是要是能自底向上查找就好了,这样就可以找到公共祖先了。

二叉树回溯的过程就是从低到上。

后序遍历(左右中)就是天然的回溯过程,可以根据左右子树的返回值,来处理中节点的逻辑。

代码:

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        # 判断当前节点root是否为None,或者是否等于p或q。如果是,则返回当前节点
        if root == q or root == p or root is None:
            return root

        # 递归地在左子树中查找p和q的最低公共祖先 
        left = self.lowestCommonAncestor(root.left, p, q)

        # 递归地在右子树中查找p和q的最低公共祖先
        right = self.lowestCommonAncestor(root.right, p, q)

        # 如果左子树和右子树都找到了节点(left和right都不为None),则说明p和q分别位于root的左右两侧,因此当前节点root就是它们的最低公共祖先
        if left is not None and right is not None:
            return root
        # 如果左子树没有找到节点(left为None),但右子树找到了节点(right不为None),则说明p和q都位于root的右子树中,或者其中一个就是root本身,返回右子树的查找结果
        if left is None and right is not None:
            return right
        # 如果右子树没有找到节点(right为None),但左子树找到了节点(left不为None),则说明p和q都位于root的左子树中,或者其中一个就是root本身,返回左子树的查找结果
        elif left is not None and right is None:
            return left
        # 如果左子树和右子树都没有找到节点(left和right都为None),则说明p和q都不在root的子树中,或者p和q都不在树中,返回None。 
        else: 
            return None

时间复杂度 O(n),空间复杂度 O(n)  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值