算法记录 | Day37 贪心算法

文章介绍了两个问题的解决方案:一是如何处理非单调递增的数字,通过后向遍历并调整数字来找到最大的单调递增整数;二是如何在二叉树中以最少的摄像头数量覆盖所有叶子节点,采用后序遍历和贪心策略来决定摄像头的放置位置。
摘要由CSDN通过智能技术生成

738.单调递增的数字

思路:

1.一旦出现strNum[i - 1] > strNum[i]的情况(非单调递增),首先想让strNum[i - 1]–,然后strNum[i]给为9,这样这个整数就是89,即小于98的最大的单调递增整数。

2.向后遍历

从前向后遍历的话,遇到strNum[i - 1] > strNum[i]的情况,让strNum[i - 1]减一,但此时如果strNum[i - 1]减一了,可能又小于strNum[i - 2]。

e.g:332,从前向后遍历的话,那么就把变成了329,此时2又小于了第一位的3了,真正的结果应该是299。

​ 后向前遍历332的数值变化为:332 -> 329 -> 299

class Solution:
    def monotoneIncreasingDigits(self, n: int) -> int:
        a = list(str(n))
        for i in range(len(a)-1,0,-1):
            if int(a[i]) < int(a[i-1]):
                a[i-1] = str(int(a[i-1])-1)
                a[i:] = '9' *(len(a)-i)
        return int("".join(a))

968.监控二叉树

思路:

让叶子节点的父节点安摄像头,所用摄像头最少,整体最优:全部摄像头数量所用最少!

可以使用后序遍历的方式遍历二叉树的节点,这样就可以优先遍历叶子节点。

对于每个节点,利用贪心思想,可以确定三种状态:

  • 第一种状态:该节点无覆盖
  • 第二种状态:该节点已经装上了摄像头
  • 第三种状态:该节点已经覆盖

为了让摄像头数量最少,要尽量让叶子节点的父节点安装摄像头,这样才能摄像头的数量最少。对此应当分析当前节点和左右两侧子节点的覆盖情况。

  • 情况1:左右节点都有覆盖

​ 左孩子有覆盖,右孩子有覆盖,那么此时中间节点应该就是无覆盖的状态了。

if left == 2 and right == 2:
   return 0

968.监控二叉树2

  • 情况2:左右节点至少有一个无覆盖的情况,如果是以下情况,则中间节点(父节点)应该放摄像头:

    • left == 0 && right == 0 左右节点无覆盖

    • left == 1 && right == 0 左节点有摄像头,右节点无覆盖

    • left == 0 && right == 1 左节点有无覆盖,右节点摄像头

    • left == 0 && right == 2 左节点无覆盖,右节点覆盖

    • left == 2 && right == 0 左节点覆盖,右节点无覆盖

这个不难理解,毕竟有一个孩子没有覆盖,父节点就应该放摄像头。

此时摄像头的数量要加一,并且return 1,代表中间节点放摄像头。

# Case 2:
    # left == 0 && right == 0 左右节点无覆盖
    # left == 1 && right == 0 左节点有摄像头,右节点无覆盖
    # left == 0 && right == 1 左节点有无覆盖,右节点摄像头
    # left == 0 && right == 2 左节点无覆盖,右节点覆盖
    # left == 2 && right == 0 左节点覆盖,右节点无覆盖
if left == 0 or right == 0:
	result += 1
return 1
  • 情况3:左右节点至少有一个有摄像头,如果是以下情况,其实就是 左右孩子节点有一个有摄像头了,那么其父节点就应该是2(覆盖的状态)

    • left == 1 && right == 2 左节点有摄像头,右节点有覆盖

    • left == 2 && right == 1 左节点有覆盖,右节点有摄像头

    • left == 1 && right == 1 左右节点都有摄像头

968.监控二叉树1

# Case 3:
    # left == 1 && right == 2 左节点有摄像头,右节点有覆盖
    # left == 2 && right == 1 左节点有覆盖,右节点有摄像头
    # left == 1 && right == 1 左右节点都有摄像头
if left == 1 or right == 1:
   return 2
  • 情况4:头结点没有覆盖,以上都处理完了,递归结束之后,可能头结点 还有一个无覆盖的情况,如图:

968.监控二叉树3

if traversal(root) == 0:
	result += 1
class Solution:
    def minCameraCover(self, root: TreeNode) -> int:
        # Greedy Algo:
        # 从下往上安装摄像头:跳过leaves这样安装数量最少,局部最优 -> 全局最优
        # 先给leaves的父节点安装,然后每隔两层节点安装一个摄像头,直到Head
        # 0: 该节点未覆盖
        # 1: 该节点有摄像头
        # 2: 该节点有覆盖

        result = 0
        # 从下往上遍历:后序(左右中)
        def traversal(curr: TreeNode) -> int:
            nonlocal result

            if not curr: return 2
            left = traversal(curr.left)
            right = traversal(curr.right)

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

            # Case 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 += 1
                return 1

            # Case 3:
                # left == 1 && right == 2 左节点有摄像头,右节点有覆盖
                # left == 2 && right == 1 左节点有覆盖,右节点有摄像头
                # left == 1 && right == 1 左右节点都有摄像头
            elif left == 1 or right == 1:
                return 2

            # 其他情况前段代码均已覆盖

        if traversal(root) == 0:
            result += 1

        return result
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值