双指针算法实战:移动零与复写零
数组原地操作是面试中的高频考点,今天通过两道经典题目来巩固双指针技巧。这两道题都要求在不使用额外空间的前提下修改数组。
一、移动零
题目描述
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。必须原地操作,尽量减少总操作次数。
思路解析
这道题的核心在于如何高效地保留非零元素并填补零。如果直接创建新数组虽然简单,但违背了原地操作的约束。
我们可以使用两个指针:
read(读指针):负责遍历整个数组,寻找非零元素。write(写指针):负责记录下一个非零元素应该放置的位置。
当 read 遇到非零元素时,将其与 write 位置的元素交换,然后 write 向前移动一步。无论是否交换,read 都会继续向后扫描。这样,所有非零元素都会被依次移到数组前端,剩余的尾部自然就是 0。
代码实现
下面是用 C 语言实现的完整逻辑,注意变量命名和边界处理:
void Swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
void moveZeroes(int* nums, int numsSize) {
int read = 0;
int write = 0;
while (read < numsSize) {
if (nums[read] != 0) {
// 只有当两个指针不重合时才需要交换,避免无谓操作
if (read != write) {
Swap(&nums[write], &nums[read]);
}
write++;
}
read++;
}
}
这段代码提交后能顺利通过测试。关键点在于理解 write 指针实际上标记了'已处理好的非零序列'的末尾,后续未处理的区域会自动被覆盖或保持为 0。
二、复写零
题目描述
在一个固定的整数数组中,将每个出现的 0 复写一遍,并将后面的元素向右移动。超出原数组长度的元素将被丢弃。
思路解析
如果从前向后遍历,遇到 0 就插入,会导致后续元素不断右移,时间复杂度会退化为 O(N^2)。为了优化效率,我们需要从后向前进行复写。
解题步骤如下:
- 统计位置:先遍历一次数组,计算在复写后的扩展数组中,最后一个有效元素的位置。这决定了我们从哪里开始倒序写入。


