Day3:LeeCode Hot3-移动零
大家好,今天开启 LeetCode Hot100 系列 的第三篇:移动零。
这道题是双指针的标杆入门题,也是算法刷题、前端/后端面试的基础必考题型。
题目看似简单,实则考察数组原地操作、双指针思想、空间复杂度优化等核心能力,掌握本题思路可以完美迁移至「数组元素移除、元素筛选」等同类题型,性价比极高。
一、题目描述
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
必须在原数组上操作,不能拷贝额外的数组,尽量减少操作次数。
示例:
输入:nums = [0,1,0,3,12]
输出:[1,3,12,0,0]
题目核心要求:
-
所有非零元素前置,保持原有相对顺序不变
-
所有零元素后置
-
原地修改数组,O(1) 额外空间
二、解题核心思路
很多初学者第一反应是遍历数组删除0、末尾补0,但普通删除会导致数组元素频繁前移,效率极低,且容易出现索引错位问题。
本题最优解题思想是快慢双指针,核心逻辑一句话概括:慢指针记录非零元素的待放置位置,快指针遍历寻找所有非零元素。
通过双指针可以一次遍历完成元素筛选,避免重复操作,完美满足原地修改、低时间复杂度的要求。
三、快慢指针解法
1. 思路分析
-
快指针 fast:全程遍历数组,逐个扫描所有元素,负责寻找非零元素。
-
慢指针 slow:指向数组左侧待填充的位置,专门用来存放筛选出来的非零元素。
遍历过程:快指针找到非零元素后,将元素赋值给慢指针位置,随后慢指针右移占位;遍历结束后,慢指针左侧全部是有序非零元素,最后将慢指针及之后的位置全部补0即可。
2. 完整代码实现(Java)
public void moveZeroes(int[] nums) {
// 慢指针:记录非零元素填充位置
int slow = 0;
// 快指针:遍历整个数组
for (int fast = 0; fast < nums.length; fast++) {
// 找到非零元素,进行填充
if (nums[fast] != 0) {
nums[slow] = nums[fast];
slow++;
}
}
// 慢指针之后的位置全部补0
for (int i = slow; i < nums.length; i++) {
nums[i] = 0;
}
}
3. 分步流程(以 nums = [0,1,0,3,12] 为例)
-
fast=0,元素为0,跳过,slow不变;
-
fast=1,元素为1,nums[slow]=1,slow=1;
-
fast=2,元素为0,跳过,slow不变;
-
fast=3,元素为3,nums[slow]=3,slow=2;
-
fast=4,元素为12,nums[slow]=12,slow=3;
-
遍历结束,slow=3,将下标3、4全部补0,最终数组 [1,3,12,0,0]。
4. 复杂度分析
-
时间复杂度:O(n),n 为数组长度,仅两次线性遍历,无嵌套循环,效率最优。
-
空间复杂度:O(1),仅使用两个指针变量,完全原地修改数组,无额外数组开销。
四、高频易错点总结
1. 破坏非零元素相对顺序
部分同学会采用首尾双指针相向遍历,快速交换零元素,但该写法会打乱非零元素顺序,不满足题目要求,面试严禁使用。
2. 忽略原地操作要求
新建数组存储非零元素、最后补0的写法虽然结果正确,但占用O(n)额外空间,不符合题目进阶要求,面试会扣分。
3. 索引越界问题
补零循环从slow开始,而非0,若从0开始会覆盖已排序的非零元素,是新手高频bug。
4. 全零/无零边界 case
两种双指针解法均可天然兼容 [0,0,0]、[1,2,3] 等边界数组,无需额外判空处理。
五、刷题总结
1. 题型定位
本题是数组双指针(快慢指针)的核心模板题,核心思想:一个指针遍历,一个指针占位筛选有效元素。
作为数组双指针的入门模板题,移动零这道题的刷题价值远不止解题本身,核心是掌握原地筛选数组元素的通用解题思维,这里对核心刷题要点做系统性总结,方便后续举一反三、固化解题模板。
2. 面试答题策略
面试优先写出基础双指针赋值解法,逻辑通俗易懂、零bug风险。
浙公网安备 33010602011771号