#283
简单
低频
双指针移动零
这是一道围绕数组展开的高频练习。建议先掌握「双指针」这套写法,再结合下方步骤讲解理解状态维护、边界处理和复杂度取舍。
数组
题目分析
这道题表面上是在处理「移动零」,但先要想清楚题目到底让你返回什么,以及过程中哪些约束必须一直满足。从题型上看,它主要在考 数组 这些能力。先别急着写代码,先把题目要求翻译成人话,明确结果是什么、约束是什么、过程里要持续维护什么。只有先把题目要求翻译成人话,后面的推荐代码才是在实现思路,而不是直接给答案。
接下来怎么看推荐代码: 带着这个理解再看推荐代码时,重点观察这条主线:用一个慢指针维护下一个非零元素应该放置的位置,快指针负责扫描数组。
推荐代码
推荐解法:双指针
时间复杂度: O(n)
空间复杂度: O(1)
核心思路: 用一个慢指针维护下一个非零元素应该放置的位置,快指针负责扫描数组。
class Solution {
public void moveZeroes(int[] nums) {
int slow = 0;
for (int fast = 0; fast < nums.length; fast++) {
if (nums[fast] != 0) {
int temp = nums[slow];
nums[slow] = nums[fast];
nums[fast] = temp;
slow++;
}
}
}
}结构化讲解
面试时怎么讲
开场思路
这题我会用双指针,把数组分成已整理非零区和待处理区。
核心过程
- 慢指针指向下一个非零应该放的位置。
- 快指针从左到右扫描整个数组。
- 遇到非零元素,就交换到慢指针位置。
- 扫描结束后,所有零自然被挤到数组后面。
复杂度总结
时间复杂度 O(n),空间复杂度 O(1)。
面试补一句:这里的慢指针语义一定要讲准,不然代码容易写乱。
核心思路
题目要求保持非零元素相对顺序,同时把所有 0 挪到末尾。最稳的办法是让慢指针始终指向“下一个该填非零数的位置”,扫描到非零就交换过去。
步骤讲解
1
快指针遍历数组
从左到右检查每个元素是否为非零。
为什么这样做:所有元素都必须被扫描一遍。
对应代码提示:for (int fast = 0; fast < nums.length; fast++) { ... }
2
非零元素前移
遇到非零元素时,与慢指针位置交换,并让慢指针右移。
为什么这样做:慢指针左侧区域始终保持为已经整理好的非零区。
对应代码提示:swap(nums, slow, fast); slow++;
易错点
直接覆盖赋值导致顺序混乱
如果处理不当,可能破坏非零元素原有相对顺序。
正确理解:使用“按扫描顺序前移”的双指针模板。
每遇到 0 就向后找非零交换
这种写法仍然能做,但逻辑更绕,也更容易漏边界。
正确理解:把慢指针解释成“下一个非零该放的位置”会简单很多。
复杂度与适用判断
时间复杂度:O(n)
空间复杂度:O(1)
比其他方案更好在哪里:比删除再补零的做法更原地,也更容易保证稳定性。
适用判断:数组题要求稳定移动、原地重排时,双指针通常是第一选择。
额外提醒
- 慢指针左侧始终是不含 0 的稳定前缀。