283. Move Zeroes

25 年 10 月 3 日 星期五
744 字
4 分钟
Screenshot 2025-10-03 at 2.31.54 pm

把 nums 当作栈

最后,在栈的末尾添加两个 0,即为答案 [1,3,12,0,0]。

为了做到 O(1) 空间复杂度,直接把 nums 当作栈,用一个变量 stackSize 表示栈的大小,初始值为 0。

入栈就是把 nums[stackSize] 置为 nums[i],然后把 stackSize 加一。

最后把 nums 中的下标从 stackSize 到 n−1 的数都置为 0。

一句话思路:用一个栈记录非零元素。将nums原地当作栈,将不为0的元素入栈,最后补0到length就行了

js
/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
// 一句话思路:用一个栈记录非零元素。将nums原地当作栈,将不为0的元素入栈,最后补0到length就行了
var moveZeroes = function (nums) {
  let stackSize = 0
  for (const n of nums) {
    if (n) {
      nums[stackSize++] = n
    }
  }
  for (let i = stackSize; i < nums.length; i++) {
    nums[i] = 0
  }
}

方法二:双指针 + 交换元素

方法一(常规双指针赋值补 0)在最坏情况下(nums 全为 0),需要遍历 nums 两次。双指针 + 交换元素可实现一次遍历完成操作,是更优解。

核心思路

将数组中的 0 视作空位,核心目标是把所有非零元素依次移到数组左侧的空位上,且保证非零元素相对顺序不变。

  • 例如 nums=[0,0,1,2],将最左侧空位与非零元素 1 交换,数组变为 [1,0,0,2];原 1 的位置成为新空位,后续继续将下一个非零元素 2 与新的最左侧空位交换即可。
  • 核心是维护最左边空位的下标,确保非零元素按顺序填充。

具体思路

  1. 定义两个指针:遍历指针 i(从左到右遍历数组,初始值 0)、空位指针 i0(指向数组中最左侧的空位,初始值 0)。

  2. 保证下标区间 [i0, i-1] 内都是空位(即 0),这是整个算法的核心性质。

  3. 遍历过程中分两种情况:

    • nums[i] ≠ 0:将当前非零元素与最左侧空位交换(swap(nums[i], nums[i0])),交换后 i0i加 1(原空位被填充,新空位右移,遍历指针继续前进)。
    • nums[i] = 0:无需交换,仅将遍历指针 i加 1(当前位置为新空位,空位指针保持不变)。

算法本质

通过一次遍历,让非零元素依次 “填充” 左侧的 0 空位,交换操作既完成了非零元素的左移,又让原非零元素位置变为新空位,全程保证非零元素的相对顺序,且仅需一次遍历即可完成所有操作。

保证下标区间 [i0,i−1] 都是空位,且 i0 指向最左边的空位。

用i去找对的数(非0数),用i0来维持对的位置(0)

js
var moveZeroes = function (nums) {
  let i0 = 0
  for (let i = 0; i < nums.length; i++) {
    if (nums[i] !== 0) {
      ;[nums[i], nums[i0]] = [nums[i0], nums[i]]
      i0++
    }
  }
}

文章标题:283. Move Zeroes

文章作者:Sirui Chen

文章链接:https://blog.siruichen.me/posts/283_move_zeroes[复制]

最后修改时间:


商业转载请联系站长获得授权,非商业转载请注明本文出处及文章链接,您可以自由地在任何媒体以任何形式复制和分发作品,也可以修改和创作,但是分发衍生作品时必须采用相同的许可协议。
本文采用CC BY-NC-SA 4.0进行许可。