Leetcode 402-移掉 K 位数字

给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。
在这里插入图片描述

题解(贪心+单调栈)

前后数字逆序,并且要删除若干个数字可以考虑单调栈

题解转载自Leetcode官方
对于两个相同长度的数字序列,最左边不同的数字决定了这两个数字的大小, 例如,对于 A=1axxx,B=1bxxx,如果 a>b 则 A>B。

基于此,我们可以知道,若要使得剩下的数字最小,需要保证靠前的数字尽可能小。
在这里插入图片描述
在这里插入图片描述
考虑从左往右增量的构造最后的答案。我们可以用一个栈维护当前的答案序列,栈中的元素代表截止到当前位置,删除不超过 k 次个数字后,所能得到的最小整数。根据之前的讨论:在使用 k 个删除次数之前,栈中的序列从栈底到栈顶单调不降。

因此,对于每个数字,如果该数字小于栈顶元素,我们就不断地弹出栈顶元素,直到

  • 栈为空
  • 或者新的栈顶元素不大于当前数字
  • 或者我们已经删除了 k 位数字

上述步骤结束后我们还需要针对一些情况做额外的处理:

  • 如果我们删除了 m 个数字且 m<k,这种情况下我们需要从序列尾部删除额外的k−m 个数字。
  • 如果最终的数字序列存在前导零,我们要删去前导零。
  • 如果最终数字序列为空,我们应该返回 0。
  • 最终,从栈底到栈顶的答案序列即为最小数。

考虑到栈的特点是后进先出,如果通过栈实现,则需要将栈内元素依次弹出然后进行翻转才能得到最小数。为了避免翻转操作,可以使用双端队列代替栈的实现。(这样做也方便进行前导零的处理)

class Solution {
    public String removeKdigits(String num, int k) {
        int len = num.length();
        Deque<Character> deque = new LinkedList<>();
        //用StringBuilder而不是String
        StringBuilder res = new StringBuilder();
        int temp=k;
        //用标志位判断数字开头是否为零,初始值应该设置为true
        boolean beginZero = true;
        for(int i=0;i<len;i++){
            //1.stack不为空且2.stack小于栈顶元素且3.k>0
            while(!deque.isEmpty()&&num.charAt(i)<deque.peekLast()&&k>0){
                deque.pollLast();
                k--;
            }
            deque.offerLast(num.charAt(i));
        }
        //1.还要考虑数组一直递增的特殊情况,所以只取栈前len-temp位 
        //2.用标志位判断数字开头是否为零
        for(int i=0;i<len-temp;i++){
            //此处要存一下deque.pollFirst(),否则下文if判断和append操作可能会使得deque弹出两次
            Character digit = deque.pollFirst();
            if(beginZero&&digit=='0') continue;
            //说明数字开头前几个为零的数字已经被跳过
            beginZero=false;
            res.append(digit);
        }
        return res.isEmpty() ? "0" : res.toString();
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值