暴力 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的任意一个数,尝试如下操作
- 如果这个数递增,返回这个数
- 否则,这个数的某个位置不递增,即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,后序数字全部改为9,然后从头开始判断
- 如果没有逆序,返回当前数字
下面是更改后的代码
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,避免重复判断数组中是否存在逆序对,题解的贪心写得很简洁很清晰