在学习了基础语法之后,是时候通过实际题目来巩固一下双指针技巧了。今天我们来拆解两道经典的数组原地操作题:移动零与复写零。
一、移动零
题目描述
给定一个数组 nums,要求将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。注意,必须在原数组上操作,不能创建新数组。
思路解析
这道题的核心在于如何在不破坏原有顺序的前提下完成交换。最直观的想法是创建一个新数组,但这违反了空间复杂度的要求。
我们可以使用两个指针:
current(快指针):负责遍历整个数组,寻找非零元素。dest(慢指针):指向当前应该放置非零元素的位置。
当 current 遇到非零元素时,将其与 dest 位置的元素交换,然后 dest 向后移动一位。无论是否交换,current 都会继续向前。这样就能保证所有非零元素按顺序被'挤'到前面,剩下的位置自然就是 0。
代码实现
void Swap(int *a, int *b) {
int temp = 0;
temp = *a;
*a = *b;
*b = temp;
}
void moveZeroes(int* nums, int numsSize) {
int current = 0;
int dest = 0;
while (current < numsSize) {
if (nums[current] != 0) {
Swap(nums + dest, nums + current);
dest++;
}
current++;
}
}
这段逻辑在力扣等平台上提交均能通过。关键点在于 Swap 函数保证了非零元素的相对顺序不被打乱。
二、复写零
题目描述
给定一个固定长度的整数数组 arr,将数组中出现的每个 0 都复写一遍,其余元素向右移动。超出数组长度的元素将被丢弃。
思路解析
如果从前往后遍历并插入 0,后面的元素会被不断覆盖,导致数据丢失。因此,正确的策略是从后往前处理。
具体步骤如下:
- 确定写入边界:先遍历一次数组,计算如果进行复写操作,最后一个有效元素会落在哪里。这决定了我们倒序复制的起始点。
- 处理边界情况:如果最后一个被复写的元素恰好是
0,且它会导致数组越界(即原本该位置是0,复写后多出一个0),则需要特殊处理,丢弃末尾的一个元素。 - 倒序复制:从确定的边界开始,向前遍历原数组。遇到 就写入两次,遇到非零元素则写入一次。


