移除K位数字——基于贪心算法和迭代的Python实现及优化

问题描述:

给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小,其中:

  • num 的长度小于 10002 且 ≥ k
  • num 不会包含任何前导零

> 示例1:
输入: num = “1432219”, k = 3
输出: “1219”
解释: 移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219。

示例2:
输入: num = “10200”, k = 1
输出: “200”
解释: 移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。

> 示例3:
输入: num = “10”, k = 2
输出: “0”
解释: 从原数字移除所有的数字,剩余为空就是0

问题分析:

一、初步思考
一个数字中移除多个位得到最小值,我们似乎不易找出规律,不如将过程看作是一位接一位有序移除的,每移除一位得到数值的减小,目的是使得减小量的总和最大。

我们进一步思考,会发现这和最短路径中的动态规划求解有些相似。因为,如果数字A(n)移除一位数字x1得到最小值为A(n-1),在A(n-1)的基础上移除一位数字x2得到最小值为A(n-2)。那么,A(n)同时移除x1, x2得到数字A(n-2)一定是移除两位后的最小值。所以,我们可以从当前数字上找出移除一位的最小值去生成下一个数字(贪心),循环迭代k次后即可得到移除原数字k位的最小值。

譬如示例一:

数字=======移除位
1432219 -------- 4
132219 ---------- 3
12219 ------------ 2
1219

二、移除一位数字得到最小值的研究

  • 不包含0的数字
    每移除一位,都是导致数字总体长度减1,此时我们只需要保证移除的高位数字尽可能小。查找方式则可以从最高位开始,查找每一位数字和下一位之间在数值上的下降。
    譬如:

===== 数字 ===== 移除位 =====

  • 123456 ---------- 6 (更高位无“数值下降”,去最低位)
  • 765432 ---------- 7(7>6)
  • 12321 ------------ 3(更高位无“数值下降”,直到3>2)
  • 包含0的数字
    包含0的数字与上述情况没有发生本质变化,只是会造成更多位的减少,譬如:

===== 数字 ==== 移除位 ===== 处理后的数字

  • 10200 ---------- 1 ------------------ 200(1>0)
  • 9801 ------------ 9 ------------------ 801(9>8)
    801 ------------- 8 ------------------ 1(8>0)
  • 30 --------------- 3(3>0)

不难发现,规律依然是上述的寻找“数值下降点”并去除。

  • 个位的处理
    前述方法从最高位向低位查找当前位与下一位的“数值下降”,而当遍历到个位时,无更低位数字,故需要补充一个低于所有数值的数,当待处理数据以字符型输入时,我们可补充‘$’字符,因为转为money可以提高我们的学习兴趣, 它的ASCII码0x24小于0的ASCII码0x30。即,

===== 数字 ==== 移除位 ===== 处理后的数字

  • “2$” -------------- 2 ------------------ “$” (ord(‘0’)>ord(’$’))

输出之前再去除“$”即可。

三、递归过程
我们已经详细分析了移除一位的过程,那么怎么递归呢? 让我们观察以下示例:

124309移除3位的过程
数字=======移除位
124309 -------- 4
12309 ---------- 3
1209 ------------ 2
109

第一次循环:1>2不成立,2>4不成立,4>3成立,移除4得到“12309$”
第二次循环:1>2不成立,2>3不成立,3>0成立,移除3得到“1209$"
第三次循环:1>2不成立,2>0成立,移除2得到“109$"
去除次数达到3,退出处理

可以发现每次循环由高位向低位查找第一个“数值下降”数并剔除,下一循环无需从最高位重新开始,可以直接由当前循环剔除数的前一位开始,这是因为本次处理仅有可能导致到前一位产生新的“数值下降”(譬如第一次循环处理后形成了新的3>0“数值下降”),而对更高位无影响。
对于长度很高,移除量K很大的任务,这可以显著降低算法的时间复杂度。

去除“$”,去除前导零,检查是否为空,若为空则输出“0”,否则直接输出
(譬如“1”移除1位,去除“$”,去除前导零得到的为“”空字符串)。

处理完毕!撰写代码,分析简化如下^-^

def removeKdigits(num, k):
    """
    :type num: str
    :type k: int
    :rtype: str
    """
    num_str = num + '$' # ASCII: 0x24 < ord('0')
    idx = 0
    for i in range(k):
        while num_str[idx] <= num_str[idx + 1]:
            idx += 1
        num_str = num_str[:idx] + num_str[idx+1:]
        idx -= 1  # back to pre-digit and restart
    # return new number
    num_str = num_str.rstrip('$')  # delete $
    num_str = num_str.lstrip('0')  # delete leading 0
    if num_str == '':
        num_str = '0'
    return num_str
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值