738.单调递增的数字,对解题代码的深度思考

前情提要

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]没有区别,只需要稍稍变动一下解题代码就行了

image-20211117102940834

思路

贪心思想:贪心思想:得到小于等于N的最大单调递增的整数。

可不可以把后面for循环去掉,直接在代码中变成 9 ?

不行

这里以num[i] > num[i+1]举例

image-20211117105536551

因为我们是要把处于末尾的的数字全变成 9 ,如果在第一个循环中直接更改,当隔了一段数字没有满足if(num[i] > num[i+1]),而从后往前快遍历完了才满足if(num[i] > num[i+1]) ,就会造成有部分数字没变成 9 。

举个例子:465791

image-20211117112816004

可以看到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));
    }
}

猜你喜欢

转载自blog.csdn.net/lihuabeats/article/details/121376110