
题目 1:移动零283. 移动零 - 力扣(LeetCode)
题目描述
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
要求:必须在原数组上操作,不能拷贝额外的数组。尽量减少操作次数。
示例:
cpp
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
思路分析(快慢指针)
- 定义慢指针 slow:指向已经处理好的区域的末尾,存放有效非 0 元素。
- 定义快指针 fast:遍历整个数组,寻找非 0 元素。
- fast 每找到一个非 0 值,就把这个值赋值给 slow 位置,slow 向后走一步。
- 遍历结束后,slow 前面全部是非 0 元素;slow 到数组末尾区间,全部填充 0。
核心思想:slow 负责 "记录有效元素存放位置",fast 负责搜索有效元素。 暴力思路:遇到 0 就整体后移,时间复杂度 O (n²),会超时;双指针 O (n)。
C++ 代码
cpp
void moveZeroes(vector<int>& nums) {
int slow = 0;
// fast遍历数组,找非0元素
for(int fast = 0; fast < nums.size(); fast++){
if(nums[fast] != 0){
nums[slow] = nums[fast];
slow++;
}
}
// slow之后全部填0
for(int i = slow; i < nums.size(); i++){
nums[i] = 0;
}
}
复杂度分析
- 时间复杂度:\(O(n)\),最多两次遍历数组
- 空间复杂度:\(O(1)\),原地修改,仅两个指针变量
题目 2:复写零1089. 复写零 - 力扣(LeetCode)
题目描述
给你一个长度固定的整数数组 arr,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。
注意:请不要在超过该数组长度的位置写入元素。要求:对输入的数组 就地 修改。
示例:
cpp
输入:arr = [1,0,2,3,0,4,5,0]
输出:[1,0,0,2,3,0,0,4]
解释:第一个0复写为两个0;第二个0复写两个0;最后的0后面超出数组长度,不扩展。
思路分析
如果从左往右直接复写:会覆盖掉还没遍历到的元素,导致数据丢失。 ✅ 正确方案:先统计有效长度,再从后往前双指针覆盖
- 先遍历一遍,统计:如果全部复写,实际会占用多少长度
len。遇到 0,长度 + 2,非 0+1。 - 两个指针:
slow指向复写完成后的最后一个位置,fast指向原数组末尾。 - 从后向前遍历:
- 如果 arr fast ≠ 0:直接把值复制到 arr slow,slow--,fast--
- 如果 arr fast == 0:slow 位置写 0,slow--;再写一个 0,slow--;fast--
- 注意:slow 不能小于 0,保证不越界。
关键点:从后往前,避免还没读取的元素被提前覆盖。这是本题最容易踩坑的地方。
C++ 代码
cpp
void duplicateZeros(vector<int>& arr) {
int n = arr.size();
int slow = 0;
// 第一步:统计复写后占用的长度
int fast;
for(fast = 0; fast < n; fast++){
if(arr[fast] == 0){
slow += 2;
}else{
slow += 1;
}
if(slow >= n) break;
}
// 从后往前覆盖
while(fast >= 0){
if(arr[fast] != 0){
arr[slow - 1] = arr[fast];
slow--;
}else{
// 遇到0,复写两个0
if(slow - 1 < n) arr[slow - 1] = 0;
if(slow - 2 < n) arr[slow - 2] = 0;
slow -= 2;
}
fast--;
}
}
复杂度分析
- 时间复杂度:\(O(n)\),两次遍历数组
- 空间复杂度:\(O(1)\),原地修改
📌 两道题对比总结(博客重点)
表格
| 题目 | 指针方向 | 核心操作 | 坑点 |
|---|---|---|---|
| 移动零 | 从左向右 | 收集非 0,尾部补 0 | 不要一边遍历一边删除元素 |
| 复写零 | 先统计,从后向前 | 遇到 0 写两个 0 | 正向遍历会覆盖未读元素 |
共同点:快慢指针,原地数组操作,不开额外数组,属于同一系列双指针题型。
题型归类:数组原地元素搬迁 / 覆盖,笔试面试很喜欢放在一起考察,区分是否真正理解双指针而不是背模板。
小结
很多同学以为双指针只有快慢指针找环、对撞指针求和。这两道原地数组题,拓展了双指针的使用场景:元素搬迁。 移动零是基础入门版本;复写零是进阶版本,增加了 "正向覆盖污染数据" 的陷阱。掌握这两道,以后遇到原地数组类题目,第一反应就可以考虑快慢指针。
