双指针算法详解
双指针是解决数组相关问题的常用技巧。这里需要明确一点,双指针里的'指针'通常指的是数组下标,而非内存地址意义上的指针。
移动零
这道题的核心在于将数组划分为两部分:保持相对顺序的非零元素和所有的零。对于这类数组划分问题,双指针非常适用。
我们可以定义 cur 指向待处理区域的第一个元素,dest 用来标记已处理区域中非零元素的末尾位置。初始时,cur 指向 0,dest 指向 -1。遍历过程中,如果当前元素不是 0,就将 dest 后移并交换元素;如果是 0,则跳过。这样就能保证所有非零元素按原顺序前移,零被留在后面。
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int dest = -1;
int cur = 0;
while (cur < nums.size()) {
if (nums[cur] != 0) {
dest++;
swap(nums[dest], nums[cur]);
}
cur++;
}
}
};
复写零
这道题涉及原地修改,不能直接从左向右遍历,否则会覆盖未处理的元素。正确的做法是先找到最后一个需要复写的数,然后从右向左进行填充。
首先使用双指针确定边界:cur 扫描数组,dest 记录复写后的逻辑长度。遇到 0 时 dest 增加 2,否则增加 1。当 dest 超出数组范围时停止。此时若 dest 刚好等于数组长度,说明最后一个是 0 且越界了,需要特殊处理,将最后一个元素置为 0 并调整指针。
确定好边界后,从后向前复制。注意处理边界情况,避免数组越界。
class Solution {
public:
void duplicateZeros(vector<int>& arr) {
int cur = 0;
int dest = -1;
int n = arr.size();
while (cur < n) {
(arr[cur] == ) dest += ;
dest += ;
(dest >= n - ) ;
cur++;
}
(dest == n) {
arr[n - ] = ;
cur--;
dest -= ;
}
(cur >= ) {
(arr[cur] == ) {
arr[dest--] = ;
arr[dest--] = ;
} {
arr[dest--] = arr[cur];
}
cur--;
}
}
};


