Leetcode算法训练日记 | day37

专题九  贪心算法

一、单调递增的数字

1.题目

Leetcode:第 738 题

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

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

示例 1:

输入: n = 10
输出: 9

示例 2:

输入: n = 1234
输出: 1234

示例 3:

输入: n = 332
输出: 299

2.解题思路

使用贪心算法解决单调递增的数字问题。

monotoneIncreasingDigits 函数中,首先将整数 N 转换为字符串 strNum。然后,我们使用两个 for 循环来找到并修改数字。第一个 for 循环从字符串的末尾开始向前遍历,寻找第一个不满足单调递增条件的位置。一旦找到这样的位置,我们记录下这个位置,并将其前一个位置的数字减 1,因为我们知道这个数字太大,导致无法通过简单地将后面的数字置为 9 来得到下一个更大的单调递增数字。第二个 for 循环从记录的位置开始,将后面的所有数字置为 9。这是因为在第一个 for 循环中,我们已经确保了前面的数字是单调递增的,所以我们只需要简单地增加后面的数字。最后,我们使用 stoi 函数将修改后的字符串 strNum 转换回整数,并返回这个新的整数。这个新的整数就是满足条件的下一个更大的单调递增数字。

3.实现代码

#include <iostream>
#include <vector>
#include <string>
using namespace std;

class Solution {
public:
    // monotoneIncreasingDigits 函数用于找到一个数字 N 的下一个更大的单调递增数字
    int monotoneIncreasingDigits(int N) {
        // 将整数 N 转换为字符串,以便逐位操作
        string strNum = to_string(N);
        // 初始化 flag 变量,用于标记从哪个位置开始可以将数字置为 9
        // 默认设置为字符串的长度,即最开始不需要置任何数字为 9
        int flag = strNum.size();

        // 从字符串的末尾开始向前遍历,找到第一个不是严格递增的位置
        for (int i = strNum.size() - 1; i > 0; i--) {
            // 如果当前位的数字大于下一位,说明找到了一个递减的位置
            if (strNum[i - 1] > strNum[i]) {
                // 记录这个位置,从这个位置开始,可以置后面的所有数字为 9
                flag = i;
                // 将当前位置的数字减 1,因为当前位置的数字太大,导致后面的数字无法通过置为 9 来增加
                strNum[i - 1]--;
            }
        }

        // 从记录的位置开始,将后面的所有数字置为 9
        for (int i = flag; i < strNum.size(); i++) {
            strNum[i] = '9';
        }

        // 将修改后的字符串转换回整数,并返回
        return stoi(strNum);
    }
};

//测试
int main()
{
    Solution p;
    int n = 1234;
    cout <<"n = " <<n<< endl;
    int result = p.monotoneIncreasingDigits(n);
    cout << "小于或等于 n 的最大数字:" << result << endl;
    cout << endl;
    return 0;
}

 

 

二、监控二叉树

1.题目

Leetcode:第 738 题

给定一个二叉树,我们在树的节点上安装摄像头。

节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。

计算监控树的所有节点所需的最小摄像头数量。

示例 1:

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

示例 2:

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

使用贪心算法解决监控二叉树问题。

minCameraCover 函数中,我们首先初始化 result 为 0,表示还没有放置任何摄像头。然后,我们调用 traversal 函数从根节点开始递归遍历二叉树。traversal 函数的返回值决定了当前节点及其子节点的覆盖状态。traversal 函数中,我们首先检查当前节点是否为空,如果为空,返回 2。然后,我们递归地遍历左子树和右子树,并获取它们的返回值。根据这些返回值,我们可以确定当前节点是否需要一个摄像头,以及如何继续递归地遍历子树。如果 traversal 函数返回 0,表示根节点及其子节点都被摄像头覆盖了,我们不需要在根节点放置摄像头。否则,我们需要在根节点放置一个摄像头,并增加 result 的值。最终,minCameraCover 函数返回 result,即覆盖整个二叉树所需的最小摄像头数量。

3.实现代码
#include <iostream>
#include <vector>
#include <string>
using namespace std;

// 定义一个结构体TreeNode,用于表示二叉树的节点。
struct TreeNode {
    int val; // 存储节点的值。
    TreeNode* left; // 指向该节点左子树的指针。
    TreeNode* right; // 指向该节点右子树的指针。
    // TreeNode的构造函数,用于创建一个TreeNode实例。
    // 参数x是节点的值,left和right默认为NULL,表示没有左右子节点。
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

class Solution {
private:
    int result; // 用于存储最终的摄像头数量

    // traversal 函数用于递归遍历二叉树,返回值有特定的含义:
    // 返回 0 表示该节点及其子节点都不需要摄像头(叶子节点或其子节点都被摄像头覆盖)
    // 返回 1 表示该节点需要一个摄像头(其子节点都被摄像头覆盖)
    // 返回 2 表示该节点不需要摄像头,但其子节点需要摄像头
    int traversal(TreeNode* cur) {
        if (cur == NULL) return 2; // 如果当前节点为空,返回 2
        int left = traversal(cur->left);    // 递归遍历左子树,并获取返回值
        int right = traversal(cur->right);  // 递归遍历右子树,并获取返回值
        // 如果左右子树返回的值都是 2,说明当前节点不需要摄像头,其子节点也不需要
        if (left == 2 && right == 2) return 0;
        // 如果左子树或右子树返回的值是 0,说明该子树被摄像头覆盖,当前节点需要一个摄像头
        else if (left == 0 || right == 0) {
            result++; // 增加摄像头数量
            return 1; // 返回 1 表示当前节点需要一个摄像头
        }
        else return 2; // 如果到这里,说明当前节点的子节点都需要摄像头,当前节点也不需要
    }

public:
    // minCameraCover 函数用于计算在二叉树上最小数量的摄像头以覆盖所有节点
    int minCameraCover(TreeNode* root) {
        result = 0; // 初始化摄像头数量为 0
        // 从根节点开始遍历二叉树
        if (traversal(root) == 0) { // 如果根节点返回 0,说明根节点无覆盖
            result++; // 根节点需要一个摄像头
        }
        return result; // 返回最终的摄像头数量
    }
};

ps:以上皆是本人在探索算法旅途中的浅薄见解,诚挚地希望得到各位的宝贵意见与悉心指导,若有不足或谬误之处,还请多多指教。 

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值