前言
这篇文章介绍了第一个专题——双指针,读者可以从我的Github查看源码以及题目链接和笔记
双指针法
首先,我们需要理解什么是双指针。双指针的应用场景往往是数组,或者说是连续的情况,比如字符串,另一个特征就是涉及到了分块,也就是一块连续的内容被分块解决,还有指针不一定是指针,往往是下标,当然具体我们结合题目分析。
Pro_01 移动零
写一道算法题我们需要有三个步骤,首先是读题,通过题干不难理解题意,不过本题的难点是规定对数组原地操作而且不能复制,作者下面图文结合给读者进行讲解:
如图作者将一串数组分成了三个部分,其实这是一个双指针的经典应用,首先本题必须改造数组本身,其次本质上有三个部分:处理的0,处理的非0以及未处理部分,结合这个想法不难想出用两个指针将数组分成三部分,然后我们开始思考两个指针怎么动,其实很简单:
如图不难看出dest和cur所代表的意义,如果现在的目的是把0放在末尾,那么当cur往后遍历遇到非0时,应该让这个非0元素和0互换位置,这样才能保证0一定在非0元素的后面,如此推理,其实本题就迎刃而解了:
cur从前往后遍历过程中:
- 遇到0:cur++
- 遇到非0:swap(dest+1,cur)
dest++,cur++
最后完成代码:
class Solution {
public:
void moveZeroes(vector<int>& nums) {
for(int dest=-1,cur=0;cur<nums.size();cur++)
{
if(nums[cur])
{
swap(nums[++dest], nums[cur]);
}
}
}
};
Pro_02 复写零
先涂镀:遇到0就写两次,一直到结束,不过需要对数组就地操作,对于这种就地操作的,我们通常先进行异地操作然后把异地操作转到就地操作
然后进行算法思路:首先考虑异地操作,两个指针即可解决,然后考虑就地操作,首先考虑正常情况下的cur和dest,也就是从前往后遍历,但是由于cur碰到0的时候会导致dest向后移动两位,这也就导致了会有值被覆盖,因此这样是错误的,所以我们尝试从后往前走,这时候我们就要考虑cur和dest的位置了:通过给的示例,我们可以反过来看,把cur起始位置定义到复写后的最后一个数,dest直接指向最后一个数就行,然后从后向前进行复写操作即可,通过观察不难得出这样是行得通的,下面归纳一下算法步骤:
- 确定复写的最后一个位置
- 定义cur,dest
- 从后往前进行相关操作
- 注意边界条件
class Solution {
public:
void duplicateZeros(vector<int>& arr) {
int cur=0,dest=-1;
int n=arr.size();
//先找到最后一个需要复写的数
while(cur<n)
{
if(arr[cur]==0) dest+=2;
else dest+=1;
if(dest>=n-1) break;
cur++;
}
//边界
if(dest==n)
{
arr[n-1]=0;
cur--;dest-=2;
}
while(cur>=0)
{
if(arr[cur] != 0) arr[dest--]=arr[cur--];
else{
arr[dest--]=0;
arr[dest--]=0;
cur--;
}
}
}
};
结语
下面会继续更新,希望读者持续关注