前情提要
Java题解
根据 carl 哥思路的解题代码(我做了部分细节上的优化)
class Solution {
public int monotoneIncreasingDigits(int n) {
// 将n转换成char数组方便操作每个位上的值大小
char[] num=Integer.toString(n).toCharArray();
// 需要变成9的位数
int start=num.length;
// 全局最优:得到小于等于N的最大单调递增的整数。
for(int i=num.length-1; i>0; i--){
if(num[i-1] > num[i]){
start=i;
num[i-1]--;
}
}
for(int i=start; i<num.length; i++){
num[i]='9';
}
return Integer.valueOf(new String(num));
}
}
num[i-1]>num[i] 和 num[i]>num[i+1] 有区别吗
carl哥有些题解用的是num[i-1]>num[i]
,有些题解用的是num[i]>num[i+1]
,我该用哪种呢,以及为什么要用这种呢?
其实num[i-1]>num[i]和num[i]>num[i+1]没有区别,只需要稍稍变动一下解题代码就行了
思路
贪心思想:贪心思想:得到小于等于N的最大单调递增的整数。
可不可以把后面for循环去掉,直接在代码中变成 9 ?
不行
这里以num[i] > num[i+1]
举例
因为我们是要把处于末尾的的数字全变成 9 ,如果在第一个循环中直接更改,当隔了一段数字没有满足if(num[i] > num[i+1])
,而从后往前快遍历完了才满足if(num[i] > num[i+1])
,就会造成有部分数字没变成 9 。
举个例子:465791
可以看到i=2时,6 > 5 , 因此 i=3 需要变成 9 ,但是它变成 9 了, 后面的 7、8 却没变成 9 。所以不能一边遍历一边更新数组元素为 9 。
我偏要正序遍历,还可以优化!!!
贪心思路
贪心思路:找到第一个满足if(num[i] > num[i+1])
,num[i]–,然后把从这个 i 位置开始后面的数字都变为 9 。如果出现 9998 这种情况,第一个满足条件的 i 的下标位置应该是 0 。
为什么 carl 哥题解从前往后遍历不行?
carl哥的写法,从前往后遍历,之所以不行(这里以 Java 版本代码的 start 来代替 carl 哥的 flag )
- 一方面,从前往后遍历,start 一直在被覆盖,start 的值并不是第一次满足条件的 i + 1的值,而从后往前遍历正好能记录到第一次满足条件的 i+1 的位置
- 另一方面,如果出现 9998 这种情况,从后往前遍历 start 也正好能被覆盖到第一次满足条件的 i+1 位置,也就是下标为 0 处,如果从前往后 start 就会被覆盖成下标为 2 的位置,而不是下标为 0 的位置
提一点 start 之所以赋值为 num.length,而不是 0 ,是因为如果数字已经是一个单调递增的数字,第二个 for 循环就不会赋值了
题解
我们写出了第一版的代码,虽然减少了一定的循环次数,但是还是有冗余的循环,因为是 first+1 的位置开始赋值为 9 的,万一 first +1 这个位置的元素值恰好是 9 呢,我们应该不用赋值才对啊。
class Solution {
public int monotoneIncreasingDigits(int n) {
char[] num = Integer.toString(n).toCharArray();
int start = num.length;
// 记录数字第一次出现的位置
int first = 0;
for (int i = 0; i < num.length - 1; i++) {
first = i;
// 这里一定要 i<num.length-2 在前不然就会越界
// && 的原因
while (i < num.length - 2 && num[i] == num[i + 1]) {
i++;
}
if (num[i] > num[i + 1]) {
num[first]--;
start = first + 1;
break;
}
}
for (int i = start; i < num.length; i++) {
num[i] = '9';
}
return Integer.valueOf(new String(num));
}
}
因此我们要把 first 和 start 解耦,first 只用来记录数字第一次出现的位置,而不用来给 start 赋值
class Solution {
public int monotoneIncreasingDigits(int n) {
char[] num = Integer.toString(n).toCharArray();
// 这个start赋值为什么已经无所谓了
int start = num.length;
// 记录数字第一次出现的位置
int first = 0;
// 判断是否需要把数组中的元素变成9
boolean flag = false;
for (int i = 0; i < num.length - 1; i++) {
first = i;
start = i + 1;
while (i < num.length - 2 && num[i] == num[i + 1]) {
// 如果已经是 9 了就没必要重复赋值了
if (num[i+1] == '9') {
start++;
}
i++;
}
if (num[i] > num[i + 1]) {
num[first]--;
flag = true;
break;
}
}
for (int i = start; i < num.length && flag; i++) {
num[i] = '9';
}
return Integer.valueOf(new String(num));
}
}
循环次数为数组的长度,carl 哥题解循环次数为数组长度 + num.length - start
贴一个换成 num[i-1] 的代码
class Solution {
public int monotoneIncreasingDigits(int n) {
char[] num=Integer.toString(n).toCharArray();
// 这个start赋值为什么已经无所谓了
int start=num.length;
// 记录数字第一次出现的位置
int first=0;
// 判断是否需要把数组中的元素变成9
boolean flag=false;
for(int i=1; i<num.length; i++){
first=i-1;
start=i;
while(i<num.length-1 && num[i-1] == num[i]){
// 如果已经是 9 了就没必要重复赋值了
if(num[i]=='9'){
start++;
}
i++;
}
if(num[i-1] > num[i]){
num[first]--;
flag=true;
break;
}
}
for(int i=start; i<num.length && flag; i++){
num[i]='9';
}
return Integer.valueOf(new String(num));
}
}