移掉 K 位数字

移掉 K 位数字

1、参考资料

https://leetcode-cn.com/problems/remove-k-digits/

2、题目要求

题目描述

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

注意:

  1. num 的长度小于 10000位 且 ≥ k。
  2. 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。

3、代码思路

LeetCode 上的题解写的不错:利用栈的贪心算法

对于两个相同长度的数字序列,最左边不同的数字决定了这两个数字的大小,例如,对于 A = 1axxxB = 1bxxx,如果 a > bA > B。所以说我们尽量将最小的数字放在左边,这样才能保证删除 K 个数字后,使得剩余的数字值达到最小。知道了这个以后,我们可以想到,在删除数字时应该从左向右迭代。

比如数字序列 13254,我们现在要求删除 2 个数字,即 K=2,那么我们从左至右进行扫描,在整个过程中,我们维护一个栈 stack 用于存储筛选后的数字序列,维护一个变量 countLeft 用于记录当前还可以移除多少个数字


根据以上的描述,我们大致说一下程序的执行流程:

  1. 初始化一个 stack 用于存储数字序列,countLeft = K = 2 表示当前还剩两个数字可以移除,然后我们从左往右开始扫描数字序列
  2. 刚开始,stack 为空,直接将数字 1 压入栈中,我们此时并没有移除数字,所以无需修改 countLeft 的值
  3. 扫描到第二个数字 3,发现 3 大于此时栈顶的元素 1,我们将 3 压入栈中,此步也没有移除任何数字,所以无需修改 countLeft 的值
  4. 扫描到第三个数字 2,发现 2 小于此时栈顶的元素 3,将 2 放在 1 后面组成的数字序列,肯定要比将 3 放在 1 后面组成的数字序列小,所以,我们将 3 从栈顶移除,此步移除了数字 3,所以 countLeft-- ,然后我们继续将 2 与栈顶元素相比较,发现 2 大于此时的栈顶元素 1, 我们将 2 压入栈中
  5. 扫描到第四个数字 5,发现 5 大于此时栈顶的元素 2,我们将 5 压入栈中,此步也没有移除任何数字,所以无需修改 countLeft 的值
  6. 扫描到第三个数字 4,发现 4 小于此时栈顶的元素 5,将 4 放在 2 后面组成的数字序列,肯定要比将 5 放在 2 后面组成的数字序列小,所以,我们将 5 从栈顶移除此步移除了数字 3,所以 countLeft-- ,此时 countLeft == 0,我们已经移除了 K 个数字,退出主循环

以下是来自 LeetCode 的图解

给定输入序列 [1,2,3,4,5,2,6,4]k=4,规则在 5 触发。删除数字 5 后,规则将在数字 4 处再次触发,直到数字 3。然后,在数字 6 处,规则也被触发。

image-20200817170926269


在上述主循环之外,我们需要处理一些情况,以使解决方案更加完整:

  1. 当我们离开主循环时,我们删除了 m 个数字,这比要求的要少,即(m<k)。在极端情况下,我们不会删除循环中单调递增序列的任何数字,即 m==0。在这种情况下,我们只需要从序列尾部删除额外的 k-m 个数字。
  2. 一旦我们从序列中删除 k 位数字,可能还有一些前导零。要格式化最后的数字,我们需要去掉前导零。
  3. 我们最终可能会从序列中删除所有的数字。在这种情况下,我们应该返回零,而不是空字符串。

4、代码实现

  1. 代码

    /**
     * @ClassName RemoveKdigitsDemo
     * @Description TODO
     * @Author Heygo
     * @Date 2020/8/17 11:14
     * @Version 1.0
     */
    public class RemoveKdigitsDemo {
    
        public static void main(String[] args) {
            String minDigits = removeKdigits("13254", 2);
            System.out.println(minDigits);
        }
    
        public static String removeKdigits(String num, int k) {
            LinkedList<Character> stack = new LinkedList<Character>();
            int countLeft = k; // 还剩多少个数字可以移除
    
            // 从左往右扫描数字序列
            for (char digit : num.toCharArray()) {
                /*
                 看看当前字符(digit)是否小于栈顶元素
                    如果 stack.peekLast() > digit,那么肯定要删除 digit 左边的数字,尽量将 digit 往左放,才能使得数字序列最小
                    如果 stack.peekLast() <= digit,先将 digit 压入栈中,让它和其右边的数字进行比较
                  */
                while (stack.size() > 0 && countLeft > 0 && stack.peekLast() > digit) {
                    stack.removeLast(); // 移除栈顶元素
                    countLeft -= 1; // 可移除的数字个数减 1
                }
                stack.addLast(digit); // 将当前数字压入栈中
            }
    
            // 极端情况:如果原数字序列依次增,我们不会删除任何数字,所以这是需移除尾部元素
            for (int i = 0; i < countLeft; ++i) {
                stack.removeLast();
            }
    
            // 移除前导 0
            StringBuilder ret = new StringBuilder();
            boolean leadingZero = true;
            for (char digit : stack) {
                if (leadingZero && digit == '0') {
                    continue;
                }
                leadingZero = false;
                ret.append(digit);
            }
    
            // 如果 k 和原数字序列长度相等,则我们只能得到一个空串
            if (ret.length() == 0) {
                return "0";
            }
    
            // 返回最小的数字序列
            return ret.toString();
        }
    }
    
  2. 程序运行结果

    124
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值