Leetcode刷题笔记37:贪心6(738. 单调递增的数字、968. 监控二叉树)

86 阅读1分钟

导语

leetcode刷题笔记记录,本篇博客是贪心部分的第6期,主要记录题目包括:

Leetcode 738. 单调递增的数字

题目描述

当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是单调递增的。

给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。

示例 1:

输入: n = 10
输出: 9

示例 2:

输入: n = 1234
输出: 1234

提示:

  • 0 <= n <= 109

解法

本题使用贪心算法,以98为例,一旦出现strNum[i - 1] > strNum[i]的情况(非单调递增),首先想让strNum[i - 1]减一,strNum[i]赋值9,这样这个整数就是89。就可以很自然想到对应的贪心解法了。想到了贪心,还要考虑遍历顺序,只有从后向前遍历才能重复利用上次比较的结果。

class Solution:
    def monotoneIncreasingDigits(self, n: int) -> int:
        # 将整数 n 转换为字符列表,便于之后的处理
        string = list(str(n))
        
        # 初始化 flag,它用于记录第一个不符合单调递增条件的位置
        flag = len(string)
        
        # 从右到左遍历字符列表
        for i in range(len(string)-1, 0, -1):
            # 如果当前位置的数字小于它左边的数字,即它们不满足单调递增的条件
            if string[i] < string[i-1]:
                # 将左边的数字减一
                string[i-1] = str(int(string[i-1])-1)
                # 更新 flag 为当前位置
                flag = i
        
        # 从 flag 开始,将其右边的所有数字都设置为9
        for i in range(flag, len(string)):
            string[i] = '9'

        # 将修改后的字符列表转回整数并返回
        return int("".join(string))

Leetcode 968. 监控二叉树

题目描述

给定一个二叉树,我们在树的节点上安装摄像头。节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。 计算监控树的所有节点所需的最小摄像头数量。

 

示例 1:

输入: [0,0,null,0,0]
输出: 1
解释: 如图所示,一台摄像头足以监控所有节点。

示例 2:

输入: [0,0,null,0,null,0,null,null,0]
输出: 2
解释: 需要至少两个摄像头来监视树的所有节点。 上图显示了摄像头放置的有效位置之一。

提示:

  1. 给定树的节点数的范围是 [1, 1000]
  2. 每个节点的值都是 0。

解法

这是一个涉及到树结构和贪心的问题。为了找到需要的最小摄像头数量,可以使用递归的后序遍历方式从叶子到根来解决这个问题。

思路:

  1. 对于树的每个节点,我们有三种状态:

    • 0:此节点未被覆盖
    • 1:此节点已被摄像头覆盖,但此节点上没有摄像头
    • 2:此节点上放置了摄像头
  2. 通过后序遍历,我们首先考虑子节点,然后确定当前节点的状态。

  3. 状态转移:

    • 如果左或右子节点未被覆盖(状态为0),那么当前节点必须放置摄像头,状态变为2。
    • 如果左或右子节点放置了摄像头(状态为2),那么当前节点被摄像头覆盖,状态变为1。
    • 否则,当前节点的状态为0,表示它未被覆盖。
  4. 我们从树的叶子开始向上遍历,直到遍历到根。这样可以保证每个节点都是在其子节点之后被考虑的。

class Solution:
    def __init__(self):
        # 初始化摄像头数量为0
        self.result = 0

    def traversal(self, cur):
        # 后序遍历当前节点
        # 定义三种状态:0:未覆盖;1:有摄像头;2:已覆盖但没有摄像头;
        
        # 如果当前节点为空,返回已被覆盖状态
        # 这是因为叶子节点外部不需要摄像头覆盖
        if cur == None:
            return 2
        
        # 递归遍历左子节点,并获取其状态
        left = self.traversal(cur.left)
        # 递归遍历右子节点,并获取其状态
        right = self.traversal(cur.right)
        
        # 如果左右子节点都已被覆盖,但没有摄像头
        # 当前节点没有被覆盖
        if left == 2 and right == 2:
            return 0
        
        # 如果左或右子节点没有被覆盖
        # 在当前节点上放置一个摄像头
        # 并更新摄像头的数量
        if left == 0 or right == 0:
            self.result += 1
            return 1
        
        # 如果左或右子节点有摄像头
        # 则当前节点已被覆盖但没有摄像头
        if left == 1 or right == 1:
            return 2
        
        # 该返回值在代码中未被使用,表示未定义的状态
        return -1

    def minCameraCover(self, root: Optional[TreeNode]) -> int:
        # 重新初始化摄像头数量为0
        self.result = 0
        
        # 开始从根节点遍历整棵树
        # 如果根节点未被覆盖,再增加一个摄像头
        if self.traversal(root) == 0:
            self.result += 1
            
        # 返回所需的摄像头数量
        return self.result