双指针算法
在数据结构链表和顺序表的学习中,我们已经使用过双指针的算法思想,例如删除数组中的重复元素、判断链表是否为环、找出链表的中间节点等。双指针算法主要分为两种形式:对撞指针和快慢指针。
常见的双指针有两种形式
- 对撞指针:一般用于顺序结构中。一个指针从最左端开始,另一个从最右端开始,逐渐向中间逼近。终止条件一般为两个指针相遇或错开(left == right 或 left > right)。
- 快慢指针:又称为龟兔赛跑算法。基本思想是使用两个移动速度不同的指针在数组或链表等序列结构上移动。常用实现方式是一次循环中,慢指针向后移动一位,快指针向后移动两位。这种方法对于处理环形链表或数组非常有用。
双指针在算法题内的使用
2.1 移动零 (LeetCode 283)
题目解析
将数组内的所有零移动到非零元素之后,并保持所有非零元素的相对位置不变。
示例:输入 [1,0,3,12],输出 [1,3,12,0,0]。
算法原理讲解
该题属于数组分块类型,需将数组划分为非零区间和零区间。使用双指针时,指针可视为数组下标。
创建两个下标 cur 和 dest。cur 从左往右遍历数组,dest 指向已处理区间内最后一个非零元素的位置。数组被划分为三个区间:[0, dest](非零)、[dest+1, src-1](零)、[src, n-1](待处理)。初始时 dest = -1, src = 0。
流程逻辑如下:
- 当
cur位置元素为 0 时,只让cur++。 - 当
cur位置元素不为 0 时,先让dest++,再交换dest和cur位置的元素。
代码实现
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int dest = -1;
for(int src = 0; src < nums.size(); src++) {
if(nums[src] != 0 && ++dest != src) {
swap(nums[src], nums[dest]);
}
}
}
};
2.2 复写零 (LeetCode 1089)
题目解析
将数组中每个 0 复写一遍,非 0 元素保持不变,且要求就地实现,不能申请新内存空间。


