代码随想录算法训练营第三十一天|56. 合并区间、738.单调递增的数字、968.监控二叉树

今日题目

56. 合并区间

题目链接:56. 合并区间 - 力扣(LeetCode)

思考:本题需要对有重叠区间进行合并,最终结果存放多个无重叠的区间。首先对原始数组按照区间左边界由小到大排序,这样便于从左到右遍历区间。

每次结果保存的都是一个区间,只有当无重叠的情况才会保存该区间,这个被保存的区间需要随着遍历更新,因此初始化left和right为数组的第一个区间的左右边界。

相邻两个区间不重叠时,前面的区间就可以被保存下来,然后left和right就切换为下一个区间的左右边界。

相邻两个区间重叠时,新的左边界是两个区间左边界的最小值,新的右边界是两个区间右边界的最大值。

有一个特殊情况,那就是遍历到最后一个区间的时候,不论最后一个区间与前一个区间是否有重叠,left和right都会被更新,而这个结果还没有保存。因此遍历结束后,需要再保存一下[left, right]这个区间。

代码:

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        n = len(intervals)
        intervals.sort(key=lambda x: x[0])
        res = []
        left = intervals[0][0]
        right = intervals[0][1]
        for i in range(1,n):
            if intervals[i][0] > right: # 无重叠
                res.append([left, right])
                left = intervals[i][0]
                right = intervals[i][1]
            else:
                # 有重叠
                left = min(left, intervals[i][0])
                right = max(right, intervals[i][1])
        res.append([left, right])
        return res
738.单调递增的数字

题目链接:738. 单调递增的数字 - 力扣(LeetCode)

思考:为了满足数字所有位上单调递增,需要先依据给出的数字n进行逐位判断,从倒数第二位开始,若是此位置数字大于后面一位的数字,则此位置数字-1,直到不大于后面一位数字为止。

通过此操作使得数字满足单调递增,接下来需要让这个数字最大,即从前面处理过的最后一个位置(其左边一位数字是最后一个-1操作的)开始,由前向后,把所有数字都改为9,这样使得数字最大。

代码:

class Solution:
    def monotoneIncreasingDigits(self, n: int) -> int:
        num = str(n)
        s = len(num)
        for i in range(s-1, 0, -1):
            if num[i-1] > num[i]: # 前一位大于后一位数字
                # 减小前一位
                num = num[:i-1] + str(int(num[i-1])-1) + num[i:]
                s = i
        for i in range(s, len(num)):
            num = num[:i] + '9' + num[i+1:]

        return int(num)
968.监控二叉树

题目链接:968. 监控二叉树 - 力扣(LeetCode)

思考:拿到题目的直观感觉是,二叉树的顶层和最底层都不放摄像头,在中间层放摄像头效果会最好。但是中间层比较复杂,涉及到放置摄像头的位置左右节点的情况分析,题目分析起来难度较大。

使用三种状态标记每个节点的覆盖情况:

  • 0:该节点未被覆盖(需要被监控)

  • 1:该节点安装了摄像头(可以监控自身、父节点和子节点)

  • 2:该节点被覆盖(但没有安装摄像头)

对于空节点,返回状态 2(视为已覆盖,不影响父节点决策)。

递归左右子树,需要按照以下情况分析:

  • 情况1:左右子节点都已覆盖(状态 2),当前节点无需安装摄像头,返回 0(未覆盖)。

  • 情况2:左右子节点至少有一个未覆盖(状态 0),当前节点必须安装摄像头(状态 1),并增加摄像头计数。

  • 情况3:左右子节点至少有一个安装了摄像头(状态 1),当前节点已被覆盖(状态 2)。

代码:

# 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 minCameraCover(self, root: Optional[TreeNode]) -> int:
        # 从下往上安装摄像头:跳过leaves这样安装数量最少,局部最优 -> 全局最优
        # 先给leaves的父节点安装,然后每隔两层节点安装一个摄像头,直到Head
        # 0: 该节点未覆盖
        # 1: 该节点有摄像头
        # 2: 该节点有覆盖
        # 定义递归函数
        result = [0]  # 用于记录摄像头的安装数量
        if self.traversal(root, result) == 0:
            result[0] += 1

        return result[0]

        
    def traversal(self, cur: TreeNode, result: List[int]) -> int:
        if not cur:
            return 2

        left = self.traversal(cur.left, result)
        right = self.traversal(cur.right, result)

        # 情况1: 左右节点都有覆盖
        if left == 2 and right == 2:
            return 0

        # 情况2:
        # left == 0 && right == 0 左右节点无覆盖
        # left == 1 && right == 0 左节点有摄像头,右节点无覆盖
        # left == 0 && right == 1 左节点无覆盖,右节点有摄像头
        # left == 0 && right == 2 左节点无覆盖,右节点覆盖
        # left == 2 && right == 0 左节点覆盖,右节点无覆盖
        elif left == 0 or right == 0:
            result[0] += 1
            return 1

        # 情况3:
        # left == 1 && right == 2 左节点有摄像头,右节点有覆盖
        # left == 2 && right == 1 左节点有覆盖,右节点有摄像头
        # left == 1 && right == 1 左右节点都有摄像头
        else:
            return 2

你可能感兴趣的:(代码随想录算法训练营,算法,leetcode,数据结构,python)