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

LeetCode 738.单调递增的数字

链接:738.单调递增的数字

思路:

为了找到最大的不大于n的单调递增的数字,我们可以采用以下贪心策略:当n不为单调递增数字时,把n的前一位减1,并把后一位改成9,反复进行如上操作直到n为单调递增,即可保证结果最大。

检测是否为单调递增的函数。先从个位数取10的模,记录下当前的最大值,然后把n缩小10倍再取模,如果大于之前记录的最大值则说明不是单调递增,循环直到n的每一位数都取了模。

    bool checkIncrease(int n)
    {
        int MAX = 9;
        while (n)
        {
            int digit = n % 10;
            if (digit <= MAX)
                MAX = digit;
            else
                return false;
            n /= 10;
        }
        return true;
    }

执行贪心策略的核心代码。需要一个变量pos来表示在哪一位上进行减1操作,表示从右往左数的第pos位,需要注意的是在该位置的数字必须大于0,因为是对字符串之间操作减法的,否则会出错。

    int greedy(int n, int pos)
    {
        string num = to_string(n);
        if (num[num.size() - pos - 1] > '0')
            num[num.size() - pos - 1] --;
        num[num.size() - pos] = '9';
        return stoi(num);
    }

完整代码:

class Solution {
public:
    int monotoneIncreasingDigits(int n) {
        if (n < 10)
            return n;
        int pos = 0;
        while(!checkIncrease(n))
        {
            pos++;
            n = greedy(n, pos);
        }
        return n;
    }
    bool checkIncrease(int n)
    {
        int MAX = 9;
        while (n)
        {
            int digit = n % 10;
            if (digit <= MAX)
                MAX = digit;
            else
                return false;
            n /= 10;
        }
        return true;
    }
    int greedy(int n, int pos)
    {
        string num = to_string(n);
        if (num[num.size() - pos - 1] > '0')
            num[num.size() - pos - 1] --;
        num[num.size() - pos] = '9';
        return stoi(num);
    }
};

LeetCode 968.监控二叉树

链接:968.监控二叉树

思路:

这道题是名副其实的hard题,在看代码随想录之前一点思路也没有,虽然贪心策略很明确,就是每隔一层放置一个摄像头,叶子节点不放摄像头,但是用代码实际操作起来还是不容易的。首先我们需要给每一个节点都定义一个状态,一个节点可能的状态有以下三种:

        0:未覆盖

        1:有覆盖

        2:有摄像头

然后根据以上三种状态,给每一个节点定义一个状态转移公式。假设我们从数的底层往上层填写状态,也就是从下往上摆摄像头,所以可以用后序遍历的方式先遍历左右节点,再遍历中间节点。后序遍历的递归函数是有返回值的,返回该节点的状态,中间节点的状态可以根据左右节点返回的状态来决定。根据左右节点不同的状态,分为以下三种情况:

        1、左节点和右节点均有覆盖,中间节点无覆盖

if (left == 1 && right == 1)
    return 0;

        2、如果左节点或者右节点无覆盖,中间节点有摄像头

else if (left == 0 || right == 0)
{
    count++;
    return 2;
}

注意必须要先写这个逻辑,因为要先判断有无覆盖的情况。有可能会出现左或右节点有摄像头但是另一个节点无覆盖的情况,这种时候还是需要摄像头在该节点。比如这种情况:[0,0,0,null,null,null,0]

        3、如果左节点或者右节点有摄像头,中间节点有覆盖

else if (left == 2 || right == 2)
    return 1;

递归返回的条件为遇到空节点。那么遇到空节点应该返回什么呢?如果返回0,则说明未覆盖,中间节点(即叶子节点)就要放摄像头了,但我们不会在叶子节点放摄像头,所以不能返回0。如果返回2,则叶子节点为有覆盖状态,叶子节点的父节点为未覆盖状态,这个时候叶子节点的祖父节点才能放摄像头,也不是我们想要的。所以只能返回1,空节点为有覆盖状态,叶子节点未覆盖,这个时候就可以在叶子节点的父节点放摄像头了。

最后,有可能会出现头节点没有覆盖的情况,这个时候需要在头节点再放一个摄像头。

// 最后还需要判断根节点是否覆盖
if(!postorder(root, count))
    count++;

完整代码:

class Solution {
public:
    int minCameraCover(TreeNode* root) {
        int count = 0;
        if(!postorder(root, count))
            count++;
        // 最后还需要判断根节点是否覆盖
        return count;
    }
    int postorder(TreeNode* root, int &count)
    {
        // 0: 未覆盖
        // 1: 有覆盖
        // 2: 有摄像头
        // 空节点的话,则视为有覆盖
        if (root == nullptr)
            return 1;
        int left = postorder(root->left, count);
        int right = postorder(root->right, count);
        // 如果左节点和右节点均有覆盖,中间节点无覆盖
        if (left == 1 && right == 1)
            return 0;
        // 如果左节点或者右节点无覆盖,中间节点有摄像头
        // 必须要先写这个逻辑,因为要先判断有无覆盖的情况
        // 有可能会出现左或右节点有摄像头但是另一个节点无覆盖的情况,这种时候还是需要摄像头在该节点
        // 比如这种情况:[0,0,0,null,null,null,0]
        else if (left == 0 || right == 0)
        {
            count++;
            return 2;
        }
        // 如果左节点或者右节点有摄像头,中间节点有覆盖
        else if (left == 2 || right == 2)
            return 1;
        else
            // not reached
            return -1;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值