LeetCode探索(74):402-移掉 K 位数字

132 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

题目

给你一个以字符串表示的非负整数 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 <= 10^5
  • num 仅由若干位数字(0 - 9)组成
  • 除了 0 本身之外,num 不含任何前导零

思考

本题难度中等。

首先是读懂题意。根据题目描述我们可知,字符串 num经过删除k个字符后,返回得到的数字。考虑字符串"1432219",我们的策略是删除尽可能大的数字,比如"143"中,我们会优先删除数字4,保留数字3。依次类推,会优先删除"1432"中的"43",保留数字"12"。

对于字符串num,我们的策略是:

  • 从左至右进行遍历,当前的数字先入栈暂存。

  • 对于「依次递增」的数如12345,我们会尽量删除低位。

  • 对于「依次递减」的数如54321,我们会尽量删除高位。

所以,如果当前遍历的数字比栈顶大,符合递增,我们会让它入栈。如果当前遍历的数比栈顶小,栈顶立刻出栈,直至k为0。

最后,直至k为0时,栈中保存的数字即为答案。

解答

方法一:单调栈

/**
 * @param {string} num
 * @param {number} k
 * @return {string}
 */
var removeKdigits = function(num, k) {
  let stack = []
  // 遍历字符串num
  for(digit of num) {
    while(k > 0 && stack.length && stack[stack.length - 1] > digit) {
      stack.pop()
      k--
    }
    // 当前字符不是"0"或当前字符是"0"且栈非空(避免前导零)
    if (digit != '0' || stack.length != 0) {
      stack.push(digit)
    }
  }
  // 从stack继续删,直到k=0
  while (k > 0) {
    stack.pop()
    k--
  }
  return stack.length === 0 ? "0" : stack.join('')
}
// console.log(removeKdigits('1432219', 3)) // '1219'
// console.log(removeKdigits('10200', 1))   // '200'
// console.log(removeKdigits('9', 1))   // '0'

// 执行用时:88 ms, 在所有 JavaScript 提交中击败了22.75%的用户
// 内存消耗:46.3 MB, 在所有 JavaScript 提交中击败了75.85%的用户
// 通过测试用例:43 / 43

复杂度分析:

  • 时间复杂度:O(n),其中 n 为字符串的长度。
  • 空间复杂度:O(n)。栈存储数字需要线性的空间。

参考