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

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

文章链接
题目链接:530.二叉搜索树的最小绝对差

思路:

类似前面验证二叉树的题目。中序遍历二叉搜索树得到的为有序数组。

  1. 中序遍历二叉搜索树,数组记录其中序序列,最后在有序数组上求最小差值
  2. 在遍历过程求最小差值,使用pre指向遍历的前一个结点
    下面代码为方法2
# 递归 + 递归中更新最小值
class Solution:
    def getMinimumDifference(self, root: Optional[TreeNode]) -> int:
        self.minNum = float('inf')
        self.pre = None
        self.traversal(root)
        return self.minNum
    def traversal(self, node):
        if not node:
            return
        self.traversal(node.left)
        if self.pre:
            self.minNum = min(self.minNum, node.val - self.pre.val)
        self.pre = node
        self.traversal(node.right)


# 递归 + 函数返回二叉搜索树中的最小差值
class Solution:
    def getMinimumDifference(self, root: Optional[TreeNode]) -> int:
        self.pre = None
        minNum = self.traversal(root)
        return minNum

    def traversal(self, node):
        if not node:
            return float('inf')
        minNum = self.traversal(node.left)  # 左
        if self.pre and node.val - self.pre.val < minNum:   # 中
            minNum = node.val - self.pre.val
        self.pre = node     # 更新self.pre
        rightMin = self.traversal(node.right)   # 右
        if minNum > rightMin:
            minNum = rightMin
        return minNum

        

# 迭代
class Solution:
    def getMinimumDifference(self, root: Optional[TreeNode]) -> int:
        miniNum = float('inf')
        stack = []
        cur, pre = root, None
        while cur or stack:
            if cur:
                stack.append(cur)
                cur = cur.left
            else:
                cur = stack.pop()
                # 二叉搜索树的中序遍历是有序数组
                if pre and cur.val - pre.val < miniNum:
                    miniNum = cur.val - pre.val
                pre = cur

                cur = cur.right
        return miniNum

LeetCode 501.二叉搜索树中的众数:

文章链接
题目链接:501.二叉搜索树中的众数

思路:

  1. 根据二叉搜索树的特征
    中序遍历为有序数组
  • 1.1 中序遍历得到数组,再在数组上进行判断。
# 此处循环过程中传入的count为当前结点的count
class Solution:
    def findMode(self, root: Optional[TreeNode]) -> List[int]:
        self.vec = []
        self.traversal(root)

        len_vec = len(self.vec)
        result = []
        maxCount, count = 0, 1
        for i in range(len_vec):
            if (i < len_vec - 1 and self.vec[i] != self.vec[i + 1]) or i == len_vec - 1:
                if count > maxCount:
                    maxCount = count
                    result = [self.vec[i]]
                elif count == maxCount:
                    result.append(self.vec[i])
                count = 1	# 更新count为下一个结点的count
            else:
                count += 1	# 更新count为下一个结点的count
        return result

    def traversal(self, node):
        if not node:
            return
        self.traversal(node.left)
        self.vec.append(node.val)
        self.traversal(node.right)
  • 1.2 使用双指针,pre指向之前访问的结点,此处传入的count为pre结点的count,先更新count为当前结点的,再对result进行处理
# 递归
class Solution:
    def printLits(self, plist):
        for num in plist:
            print(num)
        print()

    def findMode(self, root: Optional[TreeNode]) -> List[int]:
        self.pre = None
        self.result = []
        self.count, self.maxCount = 0, 0

        self.traversal(root)
        return self.result

    def traversal(self, node):
        if not node:
            return 
        self.traversal(node.left)
        # 更新self.count为当前node.val的个数
        if self.pre and self.pre.val == node.val:
            self.count += 1
        elif self.pre and self.pre.val != node.val:
            self.count = 1
        else:   # 第一个遍历的结点
            self.count = 1

        self.pre = node	# 容易忘记
        
        # 是否更新result
        if self.count > self.maxCount:
            self.maxCount = self.count
            self.result = [node.val]
        elif self.count == self.maxCount:
            self.result.append(node.val)
        
        self.traversal(node.right)


#迭代
class Solution:
    def findMode(self, root: Optional[TreeNode]) -> List[int]:
        pre = None
        maxCount, count = 0, 0
        result = []

        stack = []
        cur = root
        while cur or stack:
            if cur:
                stack.append(cur)
                cur = cur.left
            else:
                cur = stack.pop()

                # 更新count
                if pre and pre.val == cur.val:
                    count += 1
                else:
                    count = 1
                # 更新pre
                pre = cur
                # 更新result
                if count > maxCount:
                    maxCount = count
                    result = [cur.val]
                elif count == maxCount:
                    result.append(cur.val)
                # 访问下一个结点
                cur = cur.right
        
        return result
        
  1. 针对一般二叉树的方法
    遍历二叉树,统计value和对应的频率为map,反转频率为key,值为value,最后对key进行排序
from collections import defaultdict
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def findMode(self, root: Optional[TreeNode]) -> List[int]:
        numFreMap = {}
        stack = [root]
        # 遍历得到map
        while stack:
            cur = stack.pop()
            numFreMap[cur.val] = numFreMap.get(cur.val, 0) + 1
            if cur.right: stack.append(cur.right)
            if cur.left: stack.append(cur.left)
        # 字典的键值翻转
        freNumMap = defaultdict(list)
        for value, fre in numFreMap.items():
            freNumMap[fre].append(value)
        # 排序频率
        frequencies = list(freNumMap.keys())
        frequencies.sort(reverse=True)
        return freNumMap[frequencies[0]]  

感悟:

  1. 使用双指针pre和cur对二叉搜索树进行中序遍历时,如果对结点的处理比较复杂,pre的更新容易忘记
  2. 计数count需要清楚传入的count是cur结点的,还是pre结点的;如果是当前结点的,对当前结点进行处理后,下一次循环前要更新count;如果是pre结点的,需要先处理count为cur结点的,再对当前结点进行处理

学习时间:

提示:这里可以添加计划学习的时间

例如:

  • 周一至周五晚上 7 点—晚上9点
  • 周六上午 9 点-上午 11 点
  • 周日下午 3 点-下午 6 点

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

文章链接
题目链接: 236.二叉树的最近公共祖先

思路:

使用回溯和后序遍历完成从底向上搜索的过程
最近公共祖先一共有两种情况
1)如下图,p,q分别在其左右子树
在这里插入图片描述
2)p / q其中一个就是公共祖先
在这里插入图片描述

  1. 使用回溯保存路径
    使用回溯得到根节点到p / q的路径。
    ① 参数和返回值:传入node 和路径path(记录的是包含了当前节点的路径),因为有全局变量result保存目标结点的路径,不需要返回值
    ② 边界条件:空结点,当前结点为p or q,处理之后返回(对于情况1,p和q的路径都会被记录,对于情况2,只需要记录其中一个节点的路径,因为那个节点就是最近公共祖先)
    ③ 正常递归:处理左右子树,加剪枝

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        self.p, self.q = p, q
        self.result = []
        self.traversal(root, [root])

        if len(self.result) == 1:
            return self.result[0][-2]
    
        path1, path2 = self.result[0], self.result[1]
        i = 0
        while path1[i] and path2[i] and path1[i] == path2[i]:
            i += 1
        return path1[i - 1]
        #return []



    def traversal(self, node, path):
        #self.printList(path)

        if node == self.p or node == self.q:
            #print("find")
            #print()
            path.append(None)
            self.result.append(path[:])
            path.pop()
            return

        if not node.left and not node.right:
            return
        
        if len(self.result) == 2:   return  # 剪枝

        if node.left: self.traversal(node.left, path + [node.left])         #隐形回溯
        if node.right: self.traversal(node.right, path + [node.right])
        

    def printList(self, plist):
        for node in plist:
            print(node.val)
        print()   
  1. 使用回溯返回最近公共节点
    采用后序遍历且需要返回最近公共节点,需要将整个二叉树遍历完成
    ① 参数和返回值:传入node,返回节点
    ② 边界条件:空结点,当前节点为p,或q,直接返回
    ③ 正常递归:首先递归左右子树,且保存返回值。此时当前节点有三种情况,
    1)左右子树返回值都不为空,当前节点为最近公共子节点,返回当前节点
    2)左右子树一个返回值为空,另一个不为空,当前节点为最近公共子节点的祖先节点,返回子树的返回值
    3)左右子树返回值均为空,当前节点与最近公共子节点无关,返回None
class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        self.p, self.q = p, q
        return self.traversal(root)

    def traversal(self, node):
        if not node or node == self.p or node == self.q:
            return node
        # 后序遍历
        leftN = self.traversal(node.left)
        rightN = self.traversal(node.right)
        if leftN and rightN:
            return node
        elif leftN and not rightN:
            return leftN
        elif not leftN and rightN:
            return rightN
        else:
            return None
        

感悟:

回溯参数包含path时,要明确path是否包含当前节点node,换句话说,就是传入参数path是当前节点的,还是前一个结点的;需要在结点处理前更新还是结点处理后更新。
最近公共子节点还可以研究一下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值