力扣 283. 移动零|双指针原地解题思路详解

1 阅读3分钟

大家好,今天给大家分享一道经典的数组原地操作题 ——LeetCode 283. 移动零,这道题是双指针技巧的入门必刷题,既能锻炼对数组的操作逻辑,也能帮你理解 “原地修改” 和 “空间复杂度优化” 的核心思路。


题目描述

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。要求:必须在不复制数组的情况下,原地对数组进行操作。

示例 1:输入:nums = [0,1,0,3,12]输出:[1,3,12,0,0]

示例 2:输入:nums = [0]输出:[0]


解题思路:快慢双指针法

你的写法是非常标准的快慢双指针解法,核心逻辑是用两个指针各司其职,实现 “非零元素前置,零元素后置” 的原地操作。

1. 核心思想拆解

  • 慢指针 slow:记录下一个非零元素应该存放的位置,初始值为 0。
  • 快指针 fast:遍历整个数组,寻找所有非零元素,初始值也为 0。

当快指针遍历到非零元素时,就把这个元素放到慢指针指向的位置,然后慢指针向后移动一位。这样一轮遍历下来,数组前半部分就是按原顺序排列的所有非零元素。

最后,我们只需要从慢指针当前位置开始,把数组剩下的元素全部赋值为 0,就能完成所有零元素的后置操作。

2. 完整代码实现

javascript

运行

/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var moveZeroes = function(nums) {
    // slow 指针:记录下一个非零元素应该放的位置
    let slow = 0;

    // fast 指针:遍历整个数组,寻找非零元素
    for (let fast = 0; fast < nums.length; fast++) {
        if (nums[fast] !== 0) {
            // 找到非零元素,放到 slow 的位置
            nums[slow] = nums[fast];
            slow++;
        }
    }

    // 把 slow 之后的所有元素都置为 0
    for (let i = slow; i < nums.length; i++) {
        nums[i] = 0;
    }
};

复杂度分析

  • 时间复杂度:O (n),我们只需要对数组进行两次遍历,第一次遍历找非零元素,第二次遍历置零,总操作次数与数组长度成正比。
  • 空间复杂度:O (1),整个过程没有使用额外的数组,所有操作都在原数组上完成,符合题目 “原地修改” 的要求。

进阶优化:减少操作次数

如果想进一步优化,我们可以在遍历的同时,直接交换非零元素和零元素,这样只需要一次遍历就能完成操作,减少置零的步骤:

javascript

运行

var moveZeroes = function(nums) {
    let slow = 0;
    for (let fast = 0; fast < nums.length; fast++) {
        if (nums[fast] !== 0) {
            // 交换非零元素和当前 slow 位置的元素
            [nums[slow], nums[fast]] = [nums[fast], nums[slow]];
            slow++;
        }
    }
};

这种写法的时间复杂度同样是 O (n),但减少了一次循环,操作次数更少,也更符合进阶题目的要求。


总结

移动零这道题的核心,就是利用双指针实现 “非零元素前置” 的原地操作。你的初始写法逻辑清晰,容易理解,是新手入门双指针的绝佳练习。掌握这种 “快慢指针各司其职” 的思路,后续很多数组类题目(如移除元素、删除重复项)都能迎刃而解。