第三十七天| 738.单调递增的数字、968.监控二叉树

Leetcode 738.单调递增的数字

题目链接:738 单调递增的数字

题干:当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是单调递增的。给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。

思考:贪心法。首先可以将数字n转变为字符串num来处理。接着一旦出现num[i - 1] > num[i]的情况(非单调递增),首先想让num[i - 1]--,然后num[i]给为9。最后要确认遍历的顺序:从前往后遍历还是从后往前遍历。

从前向后遍历的话,遇到strNum[i - 1] > strNum[i]的情况,让strNum[i - 1]减一,但此时如果strNum[i - 1]减一了,可能又小于strNum[i - 2]。举例数字:332,从前向后遍历的话,那么就把变成了329,此时2又小于了第一位的3了,真正的结果应该是299。而从后向前遍历332的数值变化为:332 -> 329 -> 299。因此选取从后向前遍历。

最后代码实现的时候,存在一些技巧,可以用一个flag来标记从哪里开始赋值9。

代码:

class Solution {
public:
    int monotoneIncreasingDigits(int n) {
        string num = to_string(n);
        
        int flag = num.size();      //记录修改为9的起始位置
        for (int i = num.size() - 1; i > 0; i--) {
            if (num[i] < num[i - 1]) {
                flag = i;       //更新修改位置
                num[i - 1]--;
            }
        }

        for (int i = flag; i < num.size(); i++)     //修改标记位置之后的数值
            num[i] = '9';
        return atoi(num.c_str());
    }
};

Leetcode 968.监控二叉树

题目链接:968 监控二叉树

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

思考:贪心法+递归处理。本题的难点在于贪心思路。

  • 整体思路

由题目给的案例可以观察出摄像头不放置在叶子节点。摄像头可以覆盖上中下三层,如果把摄像头放在叶子节点上,就浪费的一层的覆盖。 所以把摄像头放在叶子节点的父节点位置,才能充分利用摄像头的覆盖面积。

又因为头结点放不放摄像头也就省下一个摄像头, 叶子节点放不放摄像头省下了的摄像头数量是指数阶别的。所以我们要从下往上看,局部最优:让叶子节点的父节点安摄像头,所用摄像头最少,整体最优:全部摄像头数量所用最少!

此时,大体思路就是从低到上,先给叶子节点父节点放个摄像头,然后隔两个节点放一个摄像头,直至到二叉树头结点。

  • 处理难点

  • 确定遍历顺序

由于要从叶子节点开始处理,因此我们选取后序遍历,这样在回溯的过程中处理从下到上推理。

后序遍历代码模板:

int traversal(TreeNode* cur) {
    if (终止条件) return ;

    int left = traversal(cur->left);    // 左
    int right = traversal(cur->right);  // 右

    逻辑处理                            // 中
    return ;
}
  • 如何隔两个节点放一个摄像头

先来确定每个节点可能有几种状态:

有如下三种:

  • 该节点无覆盖(用0表示)
  • 本节点有摄像头(用1表示)
  • 本节点有覆盖(用2表示)

考虑终止条件:遇到空节点应该返回什么状态

首先空节点不能是无覆盖的状态,这样叶子节点就要放摄像头。其次空节点也不能是有摄像头的状态,这样叶子节点的父节点就没有必要放摄像头。所以空节点的状态只能是有覆盖,这样就可以在叶子节点的父节点放摄像头。


考虑单层递归处理逻辑:节点的四种情况
  • 情况1:左右节点至少有一个无覆盖的情况

如果是以下情况,则中间节点(父节点)应该放摄像头:

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

此时摄像头的数量要加一,返回状态为1

  • 情况2:左右节点至少有一个有摄像头

如果是以下情况,则中间节点(父节点)应该是覆盖的状态:

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

此时返回状态为2

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

以上的剩余情况:左孩子有覆盖,右孩子有覆盖,那么此时中间节点应该就是无覆盖的状态,返回状态为0。

  • 情况4:头结点没有覆盖

以上都处理完了,递归结束之后,可能头结点 还有一个无覆盖的情况,如图:

所以递归结束之后,还要判断根节点,如果没有覆盖,result++

代码:

class Solution {
public:
    int result = 0;     //记录摄像头个数

    //0表示无覆盖, 1表示有摄像头, 2表示有覆盖
    int traversal(TreeNode* node) {
        if (!node)  return 2;       //空节点显示为有覆盖
        
        int left = traversal(node->left);       //左
        int right = traversal(node->right);     //右

        if (left == 0 || right == 0) {      //左右孩子节点存在未覆盖的情况
            result++;
            return 1;
        } else if (left == 1 || right == 1)        //左右孩子存在摄像头
            return 2;
        else        //左右孩子均为有覆盖
            return 0;
    }

    int minCameraCover(TreeNode* root) {
        result = 0;
        if (traversal(root) == 0)       //根root未覆盖
            result++;
        return result;
    }
};

贪心专题的总结:

  • 贪心的本质是选择每一阶段的局部最优,从而达到全局最优
  • 两个维度权衡问题的精髓在于排序,难点在于排序顺序,优先处理哪一个维度。
  • 贪心无套路,也没有框架之类的,需要多看多练培养感觉才能想到贪心的思路。考虑局部最优时要想想是否存在反例
  • 24
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值