【leetcode刷刷】530.二叉搜索树的最小绝对差 、501.二叉搜索树中的众数 、236. 二叉树的最近公共祖先

今天做的都是二叉搜索树,主要解题思路

  1. 中序遍历+递归
  2. 双指针

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

  1. 自己写的时候用的中序遍数组+求差
  2. 题解的方法还加了一个双指针,大概意思就是记录cur和pre,因为是中序遍历,因此pre就是递增序列中cur的前一个。需要注意一定是中序遍历,cur的处理在left和right中间。
class Solution:
    def getMinimumDifference(self, root: Optional[TreeNode]) -> int:
        # 二叉搜索树
        # 最小差值
        # 1. 中序遍历转换为数组+求差
        self.diff = []
        self.traversal(root)
        return min(self.diff)
        
        
    # 返回最小值的话,需要知道左的最大值和右的最小值
    def traversal(self, root):
        if not root:
            return []
        left = self.traversal(root.left)
        right = self.traversal(root.right)
        if len(left) > 0: self.diff.append(root.val - left[-1])
        if len(right) > 0: self.diff.append(right[0] - root.val)
        return  left + [root.val] + right
class Solution:
    def getMinimumDifference(self, root: Optional[TreeNode]) -> int:
        # 二叉搜索树
        # 最小差值
        # 1. 中序遍历转换为数组+求差
        self.diff = []
        self.pre = None
        self.cur = root
        self.result = float('inf')
        self.traversal(root)
        return self.result
        
        
    # 返回最小值的话,需要知道左的最大值和右的最小值
    def traversal(self, root):
        if not root:
            return []
        self.traversal(root.left)

        if self.pre:
            self.result = min(root.val - self.pre.val, self.result)
        self.pre = root

        self.traversal(root.right)    

501.二叉搜索树中的众数

  1. 本来以为和前一题一模一样,双指针。但是写的时候出了很多错,写了一堆if else,很离谱
  2. 看了一下题解思路应该是差不多的,细节上稍有不同
class Solution:
    def findMode(self, root: Optional[TreeNode]) -> List[int]:
        # 双指针的话,记录前一个,如果一样的话count+1,并且和max_count做对比
        self.pre = None
        self.max_count = 1
        self.count = 1
        self.res = []
        self.traversal(root)
        # print(self.max_count)
        return self.res

    def traversal(self, cur):
        if cur is None:
            return 
        self.traversal(cur.left)

        print(self.pre, cur.val)
        if self.pre:
            # print(self.pre.val, cur.val)
            if self.pre.val == cur.val: 
                self.count += 1
            else: 
                self.count = 1

            if self.count > self.max_count:
                self.res = [cur.val]
                self.max_count = self.count
            elif self.count == self.max_count:
                self.res.append(cur.val)
        else: self.res = [cur.val]

        self.pre = cur

        self.traversal(cur.right)
  1. 如果不是二叉搜索树,暴力解法就是遍历一遍二叉树,得到每个数字的频率,再求最大的。
  2. dict的最大值max(map.values())
  3. key, freq in freq_map.items()遍历dict
max_freq = max(freq_map.values())
for key, freq in freq_map.items():
    if freq == max_freq:
        result.append(key)
return result

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

  1. 这题之前应该做过,但完全想不起来之前咋做的了(看了一下提交记录,感觉要么是乱做的,要么是看题解做的。。。)
  2. 现在用的递归,很复杂
  3. 题解里的思路:没看过还是挺难写出来的
    • 求最小公共祖先,需要从底向上遍历,那么二叉树,只能通过后序遍历(即:回溯)实现从底向上的遍历方式。
    • 在回溯的过程中,必然要遍历整棵二叉树,即使已经找到结果了,依然要把其他节点遍历完,因为要使用递归函数的返回值(也就是代码中的left和right)做逻辑判断。
    • 要理解如果返回值left为空,right不为空为什么要返回right,为什么可以用返回right传给上一层结果。
    • 可以说这里每一步,都是有难度的,都需要对二叉树,递归和回溯有一定的理解。
    • 本题没有给出迭代法,因为迭代法不适合模拟回溯的过程。理解递归的解法就够了。

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        self.p = p
        self.q = q
        self.res = None
        self.traversal(root)
        
        return self.res

    def traversal(self, cur):
        if not cur:
            return False, False
        
        find_p_left, find_q_left = self.traversal(cur.left)
        find_p_right, find_q_right = self.traversal(cur.right)

        print(cur.val, find_p_left, find_p_right, find_q_left, find_q_right)

        if (find_p_left or find_p_right or (cur.val == self.p.val)) and (find_q_left or find_q_right or (cur.val == self.q.val)):
            if not self.res:
                self.res = cur

        return (find_p_left or find_p_right or (cur.val == self.p.val)), (find_q_left or find_q_right or (cur.val == self.q.val))
        
        

题解的写法:

  1. 返回值只有一个,也就是最终需要的祖先
  2. 当遇到p和q的时候,返回p和q
  3. 在处理当前节点的时候,如果左右分别都有返回值,那自身一定是最近公共祖先,返回自身。此时相当于合并了两个有返回值的节点,这之后一定只有一边有返回值。
  4. 如果左右只有一边有返回值,有两种情况,一个是确实只遇到了一个,一个是两个返回值已经合并。第二种情况的话,应该继续返回之前的返回值,因为那个已经是找到的最近公共祖先了。也就是返回非空的那个。此时考虑第一种情况,也没有问题,因为第一种情况只需要有返回值就可以了,不需要关注返回的具体值,之后合并的时候才需要考虑。
  5. 如果自身是最近公共节点,那么由于是后序遍历,后一个返回值会覆盖前一个,相当于做了一次合并,这之后只有这一个返回值会回溯到顶,因此也可以。
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

你可能感兴趣的:(leetcode,算法,职场和发展)