代码随想录算法训练营第三十七天|738.单调递增的数字、968.监控二叉树 ★

738.单调递增的数字 ★

文档讲解 : 代码随想录 - 738.单调递增的数字
状态:再次回顾。(★:需要多次回顾并重点回顾。)

思路:
选择从后向前遍历。
局部最优: strNum[i - 1] > strNum[i]时,strNum[i - 1]--并将strNum[i]置为9,可保证当前数字最大;
全局最优: 小于等于N的最大单调递增的整数。

本题代码

class Solution {
public:
    int monotoneIncreasingDigits(int N) {
        string strNum = to_string(N);
        // flag用来标记赋值9从哪里开始
        // 设置为这个默认值,为了防止第二个for循环在flag没有被赋值的情况下执行
        int flag = strNum.size();
        for (int i = strNum.size() - 1; i > 0; i--) {
            if (strNum[i - 1] > strNum[i] ) {
                flag = i;
                strNum[i - 1]--;
            }
        }
        for (int i = flag; i < strNum.size(); i++) {
            strNum[i] = '9';
        }
        return stoi(strNum);
    }
};
  • 时间复杂度:O(n),n 为数字长度
  • 空间复杂度:O(n),需要一个字符串,转化为字符串操作更方便

968.监控二叉树 ★

文档讲解 : 代码随想录 - 968.监控二叉树
状态:再次回顾。(★:需要多次回顾并重点回顾。)

本题难点:

  1. 二叉树的遍历
  2. 如何隔两个节点放一个摄像头

1. 二叉树的遍历:

后序遍历,可以在回溯的过程中从下到上进行推导。

2. 如何隔两个节点放一个摄像头:

节点有三种状态:

  • 0:该节点无覆盖
  • 1:本节点有摄像头
  • 2:本节点有覆盖

首先,空节点的状态只能是有覆盖,这样就可以在叶子节点的父节点放摄像头了。

单层逻辑主要存在以下四种情况:

  • 情况一: 左右节点都有覆盖 (left == 2 && right == 2)
    应该将该节点置为无覆盖状态,也就是return 0
    左右节点都有覆盖

  • 情况二: 左右节点至少有一个无覆盖的情况 (left == 0 || right == 0)
    应该放摄像头使节点覆盖,也就是return 1,并且增加摄像头结果result++

  • 情况三: 左右节点至少有一个有摄像头 (left == 1 || right == 1)
    应该置为有覆盖状态,也就是return 2

  • 情况四: 头节点没有覆盖
    如果递归结束,头节点没有被覆盖,状态是0,那应该增加一个摄像头result++
    情况4:头结点没有覆盖

//贪心策略:局部最优:叶子节点数量最多,取叶子节点的父节点为摄像头,可以尽可能覆盖更多节点 -> 摄像头少
 //         全局最优:树的所有节点所需最小摄像头数量
class Solution {
    int result = 0;
    //0 : 无覆盖(还没被摄像头覆盖); 1:摄像头,处理结果; 2:有覆盖
    int traversal(TreeNode* cur) {
        if (cur == NULL) return 2; //只能是有覆盖状态,如果是0:无覆盖,叶子节点必须是1:摄像头;如果是1:摄像头,叶子节点必须是2:有覆盖
        // 空节点,该节点有覆盖
        if (cur == NULL) return 2;

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

        // 情况1
        // 左右节点都有覆盖
        if (left == 2 && 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 左节点覆盖,右节点无覆盖
        if (left == 0 || right == 0) {
            result++;
            return 1;
        }

        // 情况3
        // left == 1 && right == 2 左节点有摄像头,右节点有覆盖
        // left == 2 && right == 1 左节点有覆盖,右节点有摄像头
        // left == 1 && right == 1 左右节点都有摄像头
        // 其他情况前段代码均已覆盖
        if (left == 1 || right == 1) return 2;

        // 以上代码我没有使用else,主要是为了把各个分支条件展现出来,这样代码有助于读者理解
        // 这个 return -1 逻辑不会走到这里。
        return -1;
    }
public:
    int minCameraCover(TreeNode* root) {
        result = 0;
        // 情况4
        if (traversal(root) == 0) { // root 无覆盖
            result++;
        }
        return result;
    }
};
  • 时间复杂度: O(n),需要遍历二叉树上的每个节点
  • 空间复杂度: O(n)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值