代码随想录|贪心算法|22单调递增的数字

leetcode:738. 单调递增的数字 - 力扣(LeetCode)

题目

给定一个非负整数 N,找出小于或等于 N 的最大的整数,同时这个整数需要满足其各个位数上的数字是单调递增。

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

示例 1:

  • 输入: N = 10
  • 输出: 9

示例 2:

  • 输入: N = 1234
  • 输出: 1234

示例 3:

  • 输入: N = 332
  • 输出: 299

说明: N 是在 [0, 10^9] 范围内的一个整数。

思路

暴力解法

把数字N依次往前遍历(就是每次减1),然后每次都判断是不是递增数字。

如何判断一个数字是递增数字?定义一个函数,比较这个函数的相邻位大小:

bool isDigitsIncreasing(int n)
    {
        // 对于个位数及以下的数字,直接返回true,因为它们自然满足升序条件
        if (n < 10)
            return true;
    
        // 初始化前一位数字为当前数字的个位数
        int pre = n % 10;
        int cur;
    
        // 遍历整数的每一位数字
        while (n)
        {
            // 获取当前位的数字
            cur = n % 10;
    
            // 如果当前位数字大于前一位数字,则不满足升序条件,返回false
            if (cur > pre)
                return false;
    
            // 更新前一位数字为当前位数字
            pre = cur;
    
            // 移除当前位数字,继续检查下一位
            n = n / 10;
        }
    
        // 如果所有位数字都满足升序条件,则返回true
        return true;
    }

然后在便利的过程中判断,代码如下:

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

class Solution
{
private:
    bool isDigitsIncreasing(int n)
    {
        // 对于个位数及以下的数字,直接返回true,因为它们自然满足升序条件
        if (n < 10)
            return true;
    
        // 初始化前一位数字为当前数字的个位数
        int pre = n % 10;
        int cur;
    
        // 遍历整数的每一位数字
        while (n)
        {
            // 获取当前位的数字
            cur = n % 10;
    
            // 如果当前位数字大于前一位数字,则不满足升序条件,返回false
            if (cur > pre)
                return false;
    
            // 更新前一位数字为当前位数字
            pre = cur;
    
            // 移除当前位数字,继续检查下一位
            n = n / 10;
        }
    
        // 如果所有位数字都满足升序条件,则返回true
        return true;
    }

public:
    int monotoneIncreasingDigits(int n)
    {
        if (n < 10)
            return n;
        int res = 0;
        for (int i = n; i >= 0; i--)
        {
            if (isDigitsIncreasing(i))
            {
                res = i;
                break;
            }
        }
        return res;
    }
};

暴力解法超时,来看贪心解法:

以数字98举例

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

但是是从左到右遍历还是从右到左遍历呢?

以数字332为例

如果从左到右,就是329,很明显不对

如果从右到左,就是229,对的

所以我们在遍历的时候,i从nums.size()-1开始,往前遍历。

这里的贪心策略:

局部最优:相邻的两个数字位满足递增。

整体最优:整个数字满足递增。

代码如下:

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);
    }
};

注意几个点:

(1)这里需要先把整型数字转成字符串,最后再转回去。

(2)标记flag不可以初始化为0,如果数字原本就是递增的,那么第二个for循环会直接把数字的每个位全部变成9。

也不可以设置为strNum.size()-1,因为会把个位上面的数变成9. 

总结

贪心解法的思路很巧妙,但是需要注意遍历的顺序!

参考资料

 代码随想录

贪心算法,思路不难想,但代码不好写!LeetCode:738.单调自增的数字_哔哩哔哩_bilibili 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值