栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

leetcode每日一题:738. 单调递增的数字

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

leetcode每日一题:738. 单调递增的数字

一起刷题吧

一、题意分析

输入:非负整数(大于等于0)
输出:比输入小或等的,且对于这个数值,从左到右,呈单调递增(包括等)
难度:中等
标签:贪心

示例 1:
输入: N = 10
输出: 9

示例 2:
输入: N = 1234
输出: 1234

示例 3:
输入: N = 332
输出: 299

二、参考代码

这个题目,个人认为是一个数学题目,可以通过数学思维来解决。要想数值最大且满足题目要求,则需要在高位尽可能不变,低位最大的情况下去做转变,低位最大显然就是变成 9,而此时相应的高位需要减小。这里以几个特殊数值为例说明这个过程:

示例一:N = 332

  1. 个位数:2,符合题目要求,继续向前遍历,N = 33
  2. 个位数:3,3 > 2,不符合题目要求,这个时候需要做变化。秉承低位最大的原则,原来的 2 应该变为 9,高位尽可能不变,但此时需要变化,那想要最大,则应该变为比它小的最大的一个数值,当前高数是 3,比它小的最大一个数值显然是 2,因此此时值为 29, N = 3
  3. 个位数为3,此时不符合题目要求,按同样的标准做替换,值为 299
    此时 N = 0,结束循环,因此最终结果为 299

示例二:N = 4332

  1. 个位数:2,符合要求,N = 433
  2. 个位娄:3,不符合要求,转换结果为 29,N = 43
  3. 个位数:3,不符合要求,转换结果为 299,N = 4
  4. 个位数:4,不符合要求,转换结果为 3999, N = 0

示例三:N = 45443

  1. 个位数:3,符合要求,N = 4544
  2. 个位数:4,不符合要求,转换结果为 39, N = 454
  3. 个位数:4,不符合要求,转换结果为 399,N = 45
  4. 个位数:5,不符合要求,转换结果为 4999,N = 4
  5. 个位数:4,符合要求,结果为 44999,N = 0

同样的推理,大家可以试下:N = 4332 以及 N = 4321 这两组数据。通过观察,我们可以发现如果新的个位数与它前一位比较,如果符合题目中的递增要求,则直接写入在最前位即可,如果不符合,则需要做转换,转换的规律也很简单,即将原来记录的结果每一位都转换为 9,即低位最大,而当前获取的个位数减 1 之后,写入在记录的最前位即可。

通过上面的推导过程,我们知道需要记录前一位被比较的数值,同时还涉及到低位替换为 9 的过程,我们可以在遍历的过程把低位替换 9 的结果保存下来,在需要替换时直接取值即可,参考代码如下:

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

 # 前一个被比较值
 p = N % 10
 # 最后返回的结果值
 result = p
 # 相当于记录位数
 mul = 10
 # 记录将每一位替换为 9 之后的结果,方便替换时直接取值
 nine = 9
 # N 值向后移
 N = N // 10
 while N:
     r = N % 10
     if r > p:
  # 当前位变成p,已经经过的位都变成9
  result = nine + mul * (r - 1)
  p = r - 1
     else:
  result = result + mul * r
  p = r
     N = N // 10
     nine = nine + 9 * mul
     mul = mul * 10
 return result

执行情况:

执行用时:44 ms, 在所有 Python3 提交中击败了 40.91% 的用户
内存消耗:14.7 MB, 在所有 Python3 提交中击败了 5.70% 的用户

能过,但执行速度上并不是最优的。这里我们重新审视一下整个过程,可以发现这个从后往前遍历里有很多重复的转换过程,高位做了转换相当于低位做了转换,那我们是否可以从前往后遍历呢?显然也是可以的。

从前往后遍历的思路也很简单,遍历找到第一个不满足递增条件的位置,将此位置减 1,此位置之后的数值全变成 9 即可。但需要注意的是,因为涉及到有一个位置会减 1,所以可能出现减 1 之后,与前一位不再是递增关系了,因此当我们找到了第一个不满足递增条件的位置后,要从当前位置往前找,找到第一个满足减 1 之后仍然满足递增条件位置。也就是说两个寻找:

  1. 从前往后找到第一个不满足递增条件的位置
  2. 从后往前找到第一个满足减 1 后仍然满足递增条件的位置
  3. 找到位置之后的元素变成 9,当前位置减 1,就是最终结果

实现参考代码如下:

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

 N = list(str(N))
 i = 1
 # 记录最后一个满足减一之后仍然符合递增条件的位置
 pos = 0
 while i < len(N) and N[i] >= N[i - 1]:
     if N[i] > N[i - 1]:
  pos = i
     i += 1
 if i < len(N):
     inv = int(N[i])
     if inv - 1 < int(N[i - 1]):
  i = pos
     N[i] = str(int(N[i]) - 1)
     for j in range(i + 1, len(N)):
  N[j] = '9'
 return int("".join(N))

这次提交后时间上击败了 88% 的用户,看了下提交里超过 100% 的用户代码,提交了下,运行和上面代码是一样的。不过那个代码的思路也比较有意思,这里贴上代码(因为从提交中找到的,没有来源可标注):

class Solution_others:
    def monotoneIncreasingDigits(self, N: int) -> int:
 digits = []
 if N == 0:
     return 0
 while N:
     digits.append(N % 10)
     N //= 10
 digits = digits[::-1]

 marker = len(digits)  # marker是第一个需要改成9的数字
 for i in range(len(digits) - 1, 0, -1):  # 从低位到高位遍历

     if digits[i - 1] > digits[i]:
  digits[i - 1] -= 1  # 只减1,给前面的数留下更多空间,后面的数都会改成9的
  marker = i

 for i in range(marker, len(digits)):
     digits[i] = 9

 res = 0
 for i in range(len(digits)):
     res = res * 10 + digits[i]
 return res
转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号