题目描述:

解题思路:
1. 题意核心约束
- 不能打乱非零元素先后顺序;
- 只能原地修改,空间尽量 O(1);
- 零全部后置,非零全部前置。
2. 最优思想:快慢双指针
1. 快指针 fast:全程从头遍历到尾,作用:逐个扫描元素,专门寻找非零数。
2. 慢指针 slow:指向数组左侧,作用:标记下一个非零元素应该存放的位置。
3. 整体流程
- fast 遇到非零元素:把该元素放到 slow 位置,slow 向后移动;
- fast 遇到 0:直接跳过,不做处理;
- 遍历结束后,slow 左边全是有序非零数,slow 及后面全部补 0 即可。
3. 为什么不用左右对撞双指针?
左右双指针(左找0、右找非零交换)会打乱非零元素相对顺序,不符合题目要求,只能用来做“元素分割”,不适合本题。
三、我的两次踩坑与报错分析
坑1:左右双指针越界 + 顺序破坏
坑 1:左右双指针越界
第一次尝试用左右双指针思路写代码,直接报 heap-buffer-overflow 堆内存越界错误。
错误代码:
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int left = 0;
int right = nums.size() - 1;
while (left < right) {
if (nums[right] == 0) {
right++; // 致命错误:指针越界
}
if (nums[left] == 0) {
swap(nums[left], nums[right]);
left++;
right++;
}
}
}
};
坑2:快慢指针循环条件错误
第二次改用快慢指针,循环条件写错,依旧越界崩溃。
while(slow <= fast) 无数组长度限制,fast 无限自增,最终越界+死循环。
错误代码:
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int slow = 0, fast = 0;
while (slow <= fast) { // 无边界限制,必死循环+越界
if (nums[fast] != 0) {
swap(nums[slow], nums[fast]);
fast++;
slow++;
} else {
fast++;
}
}
}
};
循环条件错误:没有限制 fast 不超过数组长度,fast 会一直自增到越界。
死循环:slow 和 fast 只会变大,循环永远不停止。
四、解法一:快慢指针交换法(C++ + 思路)
解题思路
一遍遍历,fast 找非零,和 slow 位置交换;交换后 slow 右移,保证下一个非零依次往前排;零会自动被挤到后面,天然维持相对顺序。
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
void moveZeroes(vector<int>& nums) {
// slow:待存放非零元素的位置
int slow = 0;
// fast:遍历寻找非零元素
for (int fast = 0; fast < nums.size(); ++fast) {
// 找到非零,交换到前面
if (nums[fast] != 0) {
swap(nums[slow], nums[fast]);
slow++;
}
}
}
};
五、解法二:赋值补零法(C++ + 思路)
解题思路
分两步,逻辑更直白,新手好理解:
1. 第一次遍历:把所有非零元素,依次覆盖写入数组前段;
2. 记录非零元素总个数 slow,第二次遍历:从 slow 下标开始,后面全部赋值为 0。
#include <vector>
using namespace std;
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int slow = 0;
// 1. 非零元素前置
for (int fast = 0; fast < nums.size(); ++fast) {
if (nums[fast] != 0) {
nums[slow++] = nums[fast];
}
}
// 2. 末尾统一补 0
for (int i = slow; i < nums.size(); ++i) {
nums[i] = 0;
}
}
};
六、复杂度分析
- 时间复杂度:O(n)
仅遍历数组常数次,无嵌套循环;
- 空间复杂度:O(1)
仅使用两个临时指针变量,完全原地操作。
七、两种解法对比
| 解法 | 思路特点 | 难度 | 适用场景 |
|---|---|---|---|
| 快慢交换法 | 一趟遍历、原地交换、代码精简 | 中等 | 面试首选、代码简洁 |
| 赋值补零法 | 两步拆分、逻辑直白、极易理解 | 简单 | 算法入门、新手学习 |
八、新手避坑总结
1. 数组遍历必须限制下标范围,防止越界报错;
2. 双指针分工要明确:fast 找元素,slow 定位置;
3. 审题注意隐藏条件:本题强制要求「非零相对顺序不变」;
4. 双指针是数组、链表高频考点,本题是入门经典模板,建议默写熟记。
九、写在最后
LeetCode 283 是数组双指针的第一道模板题。
核心解题思路就一句话:
快指针扫描、慢指针占位,非零前置、零值后置。
吃透这道题,后续重复元素删除、数组元素移动类题目都可以用同套思想秒杀。

252

被折叠的 条评论
为什么被折叠?



