注意:双指针算法中的指针并不一定是真的语法意义上的指针,在数组中大多数情况下都是直接使用下标充当指针使用。
移动零
**思路:**这道题的本质其实就是将数组划分为两部分,一部分是保持相对顺序的非零元素,一部分是所有的 0,对于这类数组划分或者说数组分块的题基本都可以使用双指针来解决。
注意:从 [0, cur - 1] 都是已处理区域,[cur, n - 1] 是待处理区域,即 cur 指向所有待处理元素的第一个。(n 是数组元素个数)
初始状态:因为一开始待处理区域是整个数组,所以 cur 指向 0 下标位置,dest 用来划分已处理区域中的区间,但是开始时已处理区域为空,所以 dest 指向 -1 下标即可。
遍历过程:按照上图区间划分,dest 和 cur 中间应该存放元素 0,所以遇到 0 时,dest 指针不动,cur 向后移动即可(这样就将 0 放在它们两个指针中间了),如果不是 0,需要先将 dest 向后移动一个位置,然后交换两指针指向元素,然后 cur 向后移动一个位置。
代码:
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 指向数组起始位置前一个位置(即 -1),然后先判断 cur 位置的值,如果非零,dest 向后移动一步,如果是零,dest 向后移动两步,并且移动过程中判断 dest 是否移动到数组末尾,到末尾遍历结束,cur 位置就是最后一个复写的数,否则 cur 继续向后遍历。
-
从后向前复写
-
处理特殊情况
下图这种情况在找最后一个复写的数时会导致数组越界,需要进行特殊处理,可以在找完最后一个复写的数时,判断 dest 位置,如果在下标为 n(数组最后一个元素后一个位置)处,说明出现越界情况,将 n - 1 位置处的元素置 0,然后将 cur 向前移动一位,dest 向前移动两位。
代码:
class Solution {
public:
void duplicateZeros(vector<int>& arr) {
cur = ;
dest = ;
n = arr.();
(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--;
}
}
};


