leetcode
leetcode 351 ~ 400
移掉 K 位数字

移掉 K 位数字

难度:

标签:

题目描述

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

 

示例 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 。

 

提示:

  • 1 <= k <= num.length <= 105
  • num 仅由若干位数字(0 - 9)组成
  • 除了 0 本身之外,num 不含任何前导零

代码结果

运行时间: 30 ms, 内存: 17.3 MB


/*
 * 思路:
 * 使用Java Stream API,我们可以将字符串转换为字符流,
 * 然后使用一个栈来存储字符。在字符流处理过程中,
 * 对每个字符进行与栈顶元素的比较,并决定是否弹出栈顶元素。
 * 最终将栈中的字符组合成结果字符串并处理前导零。
 */
 
import java.util.Stack;
import java.util.stream.Collectors;
 
public class Solution {
    public String removeKdigits(String num, int k) {
        Stack<Character> stack = new Stack<>();
        num.chars().mapToObj(c -> (char) c).forEach(digit -> {
            while (!stack.isEmpty() && stack.peek() > digit && k > 0) {
                stack.pop();
                k--;
            }
            stack.push(digit);
        });
        while (k > 0) {
            stack.pop();
            k--;
        }
        String result = stack.stream()
                .map(String::valueOf)
                .collect(Collectors.joining());
        result = result.replaceAll("^0+(?!$)", "");
        return result.isEmpty() ? "0" : result;
    }
}

解释

方法:

这个题解使用了贪心算法和单调栈的思路。我们从左到右遍历数字,对于每个数字,如果当前数字小于栈顶元素,则弹出栈顶元素,直到栈为空或者栈顶元素小于等于当前数字。这样可以保证栈中的元素是单调递增的。最后,我们取出栈中前 remain 个元素作为结果,其中 remain 为原数字长度减去要删除的数字个数 k。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在遍历数字时,选择在当前数字小于栈顶元素时执行出栈操作?这样的操作有什么特殊意义?
在遍历数字时,执行出栈操作的目的是为了保持栈的单调递增性,从而确保最终得到的数字是尽可能小的。当当前数字小于栈顶元素时,移除栈顶元素可以去掉一个较大的数字,使得之后加入的较小数字能够更靠前,这样不仅减少了数字的总体大小,而且有效地利用了可以删除的数字个数的限制。因此,这种操作有助于实现贪心策略,即每步尽可能保持结果数字的最小性。
🦆
在贪心算法的选择过程中,如果后面的数字和栈顶元素相等,为什么没有特别的处理措施?这种情况下保留哪一个数字更优?
如果后面的数字和栈顶元素相等,算法不需要特别的处理,因为在这种情况下,无论保留哪一个数字结果都是相同的。栈的目的是保持递增顺序而非递减,因此相等的情况并不会违反这一规则。保留栈顶元素还是后面的数字对最终的结果没有影响,因为它们的数值是一样的,所以算法简单地选择继续保留栈顶的元素,而后面的数字只是简单地追加在后面。
🦆
代码中`lstrip('0') or '0'`这部分的处理是为了解决什么问题?在什么情况下会出现需要这样处理的场景?
代码中的`lstrip('0') or '0'`处理是为了去除数字字符串左侧可能出现的前导零。在移除某些数字后,尤其是最高位的数字后,可能会产生前导零,这会影响数字的正常表达。例如,从'10200'中移除一位得到'0200',经过`lstrip('0')`后变为'200'。这个处理确保了得到的结果是一个有效的数字表达。如果结果字符串全部是零,则`lstrip('0')`会返回一个空字符串,这时通过`or '0'`确保输出至少为'0',正确表示数值零。
🦆
如果输入的数字已经是最小可能值,例如 num = '12345', k = 3,这时算法的行为是什么?是否还会进行出栈操作?
如果输入的数字已经是最小可能值,例如'12345',并且需要移除'k'个数字,那么算法会依然按照流程执行,但是实际上不会进行出栈操作。因为每个后续的数字都大于前一个数字,栈顶元素永远不会大于当前考虑的数字,因此栈内数字始终保持单调递增。在这种情况下,算法最终简单地移除尾部的'k'个数字,因为这是保持数字最小的唯一方式。结果就是按照从左到右保留前'n-k'个数字。

相关问题

拼接最大数

给你两个整数数组 nums1nums2,它们的长度分别为 mn。数组 nums1nums2 分别代表两个数各位上的数字。同时你也会得到一个整数 k

请你利用这两个数组中的数字中创建一个长度为 k <= m + n 的最大数,在这个必须保留来自同一数组的数字的相对顺序。

返回代表答案的长度为 k 的数组。

 

示例 1:

输入:nums1 = [3,4,6,5], nums2 = [9,1,2,5,8,3], k = 5
输出:[9,8,6,5,3]

示例 2:

输入:nums1 = [6,7], nums2 = [6,0,4], k = 5
输出:[6,7,6,0,4]

示例 3:

输入:nums1 = [3,9], nums2 = [8,9], k = 3
输出:[9,8,9]

 

提示:

  • m == nums1.length
  • n == nums2.length
  • 1 <= m, n <= 500
  • 0 <= nums1[i], nums2[i] <= 9
  • 1 <= k <= m + n

单调递增的数字

当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是单调递增的。

给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增

 

示例 1:

输入: n = 10
输出: 9

示例 2:

输入: n = 1234
输出: 1234

示例 3:

输入: n = 332
输出: 299

 

提示:

  • 0 <= n <= 109