大家好,今天给大家分享一道经典的数组原地操作题 ——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),但减少了一次循环,操作次数更少,也更符合进阶题目的要求。
总结
移动零这道题的核心,就是利用双指针实现 “非零元素前置” 的原地操作。你的初始写法逻辑清晰,容易理解,是新手入门双指针的绝佳练习。掌握这种 “快慢指针各司其职” 的思路,后续很多数组类题目(如移除元素、删除重复项)都能迎刃而解。