双指针算法实战
双指针是处理数组和链表问题的常用技巧,主要分为对撞指针和快慢指针两种形式。
双指针基础
对撞指针常用于顺序结构,两个指针分别从两端向中间移动,直到相遇或错开。这种模式适合查找满足特定条件的两数之和等问题。
快慢指针则像龟兔赛跑,一个指针移动速度快,另一个慢。它常用于检测环形链表、寻找中点或在原地修改数组时保持相对位置关系。
01. 移动零
题目描述
给定一个数组 nums,将所有 0 移动到数组末尾,同时保持非零元素的相对顺序。必须在不复制数组的情况下原地操作。
解题思路
这道题的核心在于将数组划分为两部分:左边是非零元素,右边是零。我们可以借鉴快速排序的分区思想。
使用两个指针:
cur:遍历整个数组的扫描指针。dest:记录非零序列最后一个位置的指针。
在遍历过程中,如果 cur 指向的元素不是 0,我们就将其交换到 dest 的下一个位置,并更新 dest。这样能保证 [0, dest] 区间内全是非零元素,而 [dest+1, cur] 之间自然填充了 0。
C++ 代码实现
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int cur = 0;
int dest = -1;
while (cur < nums.size()) {
if (nums[cur]) {
swap(nums[++dest], nums[cur]);
}
cur++;
}
}
};
这段代码非常精简,本质上就是快排 partition 过程的一个变体。注意 dest 初始化为 -1,因为第一个非零元素应该放在索引 0 的位置。
02. 复写零
题目描述
在一个固定长度的整数数组中,将每个出现的 0 复写一遍,并将后续元素向右移动。超出原数组长度的元素将被丢弃。
解题思路
如果直接从前往后复写,后面的元素会被覆盖掉,导致数据丢失。因此我们需要采用从后往前的策略。
但这需要先知道哪些元素需要被保留,以及最后一个有效元素在哪里。所以流程分为两步:
- 统计阶段:先找到最后一个需要复写的元素位置(虚拟长度)。
- 写入阶段:从后往前遍历,根据当前元素是否为 决定写入一个还是两个 。


