738. 单调递增的数字

暴力 TLE

class Solution:
    def monotoneIncreasingDigits(self, N: int) -> int:
        while True:
            if self.judge(N):
                return N
            N-=1

    def judge(self,N):
        s = str(N)  
        for i in range(len(s)-1):
            if ord(s[i]) >= ord(s[i+1]):
                return False 
        return True

想都不用想 肯定会超时

贪心

对于小于10的数,直接返回N

对于大于等于10的任意一个数,尝试如下操作

  1. 如果这个数递增,返回这个数
  2. 否则,这个数的某个位置不递增,即n[i]>[i+1],i<j,那么令n[i] -1 ,n[i]之后的数都变为9
class Solution:
    def monotoneIncreasingDigits(self, N: int) -> int:
        if N<10:
            return N

        s = list(str(N))
        for i in range(len(s)-1):
            if ord(s[i]) > ord(s[i+1]):
                s[i] = chr(ord(s[i])-1)
                for j in range(i+1,len(s)):
                    s[j] = '9'
                return reduce(lambda x,y:x*10+y,list(map(int,s)))
                
        return N

但是这个策略不是很正确,因为332会输出329,但是正确答案是299

再分析一下,如果传入一个数字不是递增的,
如果最高位就不是递增,直接改变最高位,然后最高位后面的数字全部改成9,例如8777,答案是7999
如果第二位不是递增的,直接改变第二位,然后第二位后面的数字全部改成9,例如5766,答案是5699

按照以上规律可知,我们直接遍历数组,然后找到第一个不满足递增的位置,直接在这个位置开始让这串数字后面都变为递增即可,但是我们可能要重复判断多次递增,例如332,第一次改变后变为329,第二次改变后变为299,也是最终答案

总结一下贪心策略

  1. 对于数字,依次判断每一位数
  2. 如果出现逆序,即当前数大于后一个数,则当前数-1,后序数字全部改为9,然后从头开始判断
  3. 如果没有逆序,返回当前数字

下面是更改后的代码

class Solution:
    def monotoneIncreasingDigits(self, N: int) -> int:
        if N<10:
            return N

        s = list(str(N))
        flag = True
        while flag:
            flag = False
            for i in range(len(s)-1):
                if s[i] > s[i+1]:
                    s[i] = chr(ord(s[i])-1)
                    for j in range(i+1,len(s)):
                        s[j] = '9'
                    flag = True
            

        return reduce(lambda x,y:x*10+y,list(map(int,s)))

在这里插入图片描述

上面的代码涉及到字符串和int的转换,略显啰嗦,优化以下:

class Solution:
    def monotoneIncreasingDigits(self, N: int) -> int:
        if N<10:
            return N

        s = [] 
        t = N
        while t:
            s.append(t%10)
            t = t // 10
        s = s[::-1] 
        
        flag = True
        while flag:
            flag = False
            for i in range(len(s)-1):
                if s[i] > s[i+1]:
                    s[i] -= 1
                    for j in range(i+1,len(s)):
                        s[j] = 9
                    flag = True
            
        return reduce(lambda x,y:x*10+y,s)

在这里插入图片描述

时间复杂度是 O ( n ) O(n) O(n),其中n = len(N),最差情况下,原始数据基本有序,但是最后一位逆序,例如9998,变成9989,然后9899,然后8999

题解

题解在上面贪心的基础上做了一些改进,不需要反复的判断这个数字是否递增
在这里插入图片描述
就是,找到第一个逆序的位置,然后尝试修改当前数-1,由于修改后可能会导致当前数和前一个数又逆序,所以反复判断,直到遇到边界或者找到一个数字-1后与前一个数字仍然正序,那么停留于此处,然后修改后面的数

class Solution:
    def monotoneIncreasingDigits(self, N: int) -> int:
        if N<10:
            return N
        N = list(map(int,list(str(N))))
        ind = 1
        # 找到第一个逆序的位置,如果没有那么ind == len(N),如果有,那么N[ind-1] > N[ind]
        while ind<len(N) and N[ind-1] <= N[ind]:
            ind += 1
        
        # 存在逆序对
        if ind<len(N):
			# 重复-1
            while ind>0 and N[ind-1] > N[ind]:
                N[ind-1] -= 1
                ind -= 1
			
			# 从最后一个-1后的数开始,将后面的数都变为9
            ind += 1
            while ind<len(N):
                N[ind] = 9
                ind += 1
            
        return reduce(lambda x,y:x*10+y,N)

比较一下我写的贪心,就是不断往前-1,避免重复判断数组中是否存在逆序对,题解的贪心写得很简洁很清晰

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值