双指针
常⻅的双指针有两种形式,⼀种是对撞指针,⼀种是快慢指针。
对撞指针
- ⼀般⽤于顺序结构中,也称左右指针。
- 对撞指针从两端向中间移动。⼀个指针从最左端开始,另⼀个从最右端开始,然后逐渐往中间逼
近。 - 对撞指针的终⽌条件⼀般是两个指针相遇或者错开(也可能在循环内部找到结果直接跳出循
环),也就是:
left == right (两个指针指向同⼀个位置)
left > right (两个指针错开)
快慢指针
- ⼜称为⻳兔赛跑算法,其基本思想就是使⽤两个移动速度不同的指针在数组或链表等序列结构上移动。
- 这种⽅法对于处理环形链表或数组⾮常有⽤。
- 如果我们要研究的问题出现循环往复的情况时,均可考虑使⽤快慢指针的思想。
- 快慢指针的实现⽅式有很多种,最常⽤的⼀种就是:在⼀次循环中,每次让慢的指针向后移动⼀位,⽽快的指针往后移动两位,实现⼀快⼀慢。
题目解析
题目链接:283.移动零
题目很简单,就是把数组中的所有为0的元素移动数组末尾,且只能在原数组上操作还得保证非0元素的相对顺序。(ps:可以根据示例理解题目)
算法原理
这道题其实可以归类到一类题里面——数组划分(数组分块),这不是官方的名字,是本人总结出来的。
这是以上说的一类题的特点,而本题中,则将一个数组划分为两块区间。
解决这类问题,我们第一个想到的就是双指针算法,在数组中,我们通常用数组下标来充当指针使用。
根据我们的分析来举一个示例
因为刚开始的时候数组还未处理,所以让dest指向-1
当nums[cur]==0时不处理,cur++,当遇到非零元素时,++dest,再交换nums[cur]和nums[dest],最后让cur++,重复以上步骤,直到cur走到n的位置。这样就保证了[0,dest]非0,[dest+1,cur-1]为0,[cur+1,n-1]为待处理区间,符合我们的性质。
代码编写
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int cur = 0;//把数组分为两个部分,处理部分[nums[0],nums[cur-1]]和非处理部分[nums[cur],nums[nums.size()-1]]
int dest = -1;//把处理部分分为两个部分,[nums[0],nums[dest]]为非零元素,[nums[dest+1],nums[-1]]为零元素
for(;cur < nums.size();cur++)
{
//因为只遍历一遍所以时间复杂度为O(n),只定义了两个变量所有空间复杂度为O(1)
if(nums[cur])
{
swap(nums[++dest],nums[cur]);
}
}
}
};