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.
总结
贪心解法的思路很巧妙,但是需要注意遍历的顺序!