移掉 K 位数字


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


解题思路

首先咱们要了解一个关于数学的前置知识,对于两个相同长度的数字序列,最左边不一样的数字决定了这两个数字的大小,例如,对于 A = 1axxxA = 1axxx,B = 1bxxxB = 1bxxx,若是 a > b,则 A > Bgit

基于此,咱们能够知道,若要使得剩下的数字最小,须要保证靠前的数字尽量小算法

若是使用暴力法,那思路就是:app

  • 从左到右遍历
  • 对于每个遍历到的元素,前一个元素比当前元素大,则丢弃前一个元素,不然保留前一个元素

须要注意的是,若是给定的数字是一个单调递增的数字,那么咱们的算法会永远选择不丢弃。这个题目中要求的,咱们要永远确保丢弃 k 个数字,所以思路还应该稍加修改:ui

  • 每次丢弃一次,k 减去 1。当 k 减到 0 ,咱们能够提早终止遍历
  • 而当遍历完成,若是 k 仍然大于 0。不妨假设最终还剩下 x 个须要丢弃,那么咱们须要选择删除末尾 x 个元素

然而暴力的实现复杂度最差会达到 O(nk)(考虑整个数字序列是单调不降的),所以咱们须要加速这个过程code

能够用一个栈维护当前的答案序列,栈中的元素表明截止到当前位置,删除不超过 k 次个数字时,所能获得的最小整数。根据以前的讨论:在使用 k 个删除次数以前,栈中的序列从栈底到栈顶单调不降。所以,对于每一个数字,若是该数字小于栈顶元素,咱们就不断地弹出栈顶元素,直到rem

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

上述步骤结束后咱们还须要针对一些状况作额外的处理:字符串

  • 若是咱们删除了 m 个数字且 m<k,咱们须要从序列尾部删除额外的 k-m 个数字
  • 若是最终的数字序列存在前导零,咱们要删去前导零
  • 若是最终数字序列为空,咱们应该返回 0
class Solution {

    public String removeKdigits(String num, int k) {
        Deque<Character> deque = new LinkedList<>();
        for(int i = 0; i < num.length(); i++) {
            while(!deque.isEmpty() && k > 0 && deque.peekLast() > num.charAt(i)) {
                deque.pollLast();
                k--;
            }
            deque.offerLast(num.charAt(i));
        }
        for(int i = 0; i < k; i++) {
            deque.pollLast();
        }
        StringBuilder str = new StringBuilder();
        boolean leadingZero = true;
        while(!deque.isEmpty()) {
            char digist = deque.pollFirst();
            if(leadingZero && digist == '0') {
                continue;
            }
            leadingZero = false;
            str.append(digist);
        }
        return str.length() == 0 ? "0" : str.toString();
    }
}
相关文章
相关标签/搜索