跳到主要内容

LeetCode 283. 移动零

本节目标

稳定压缩全部非零元素,再原地填零。

这题是双指针解题框架中稳定压缩的典型例子:先确定前缀应该放什么,再统一处理剩余位置。

查看原题

题意与约束

把所有零移动到数组末尾,同时保持非零元素的相对顺序。函数没有返回值,必须直接修改 nums

推导与不变量

不断把一个零与后面的元素交换,会让相同元素移动多次。先扫描所有元素:任意时刻,nums[0..write) 按原顺序保存已读到的非零值。扫描结束后,这个前缀已正确,只需把 [write, n) 填为零。

原地修改

函数的结果体现在原数组 nums,返回值为 None/void。非零元素的相对顺序保持不变,尾部由填零阶段完成,额外空间为 O(1)

代码实现

两份源码先写入非零元素,再从 write 位置开始补零;第二阶段不需要知道原来每个零的位置。

C++17
#include <vector>

using namespace std;

class Solution {
public:
void moveZeroes(vector<int>& nums) {
int write = 0;
for (int read = 0; read < static_cast<int>(nums.size()); read++) {
if (nums[read] != 0) {
nums[write] = nums[read];
write++;
}
}
while (write < static_cast<int>(nums.size())) {
nums[write] = 0;
write++;
}
}
};

复杂度分析

  • 时间复杂度:O(n),两段扫描总长度不超过 2n
  • 空间复杂度:O(1)

易错点

  • 在扫描非零元素时把零提前写回,可能覆盖还未读取的值。
  • 只做压缩不补零,数组尾部会保留旧值。

模式迁移

“先稳定保留,再批量补齐”也适用于按谓词过滤数组、分离正负元素等问题。若只要求把零放到末尾而不要求相对顺序,可以改用两端交换。