专题一——双指针_Pro_01&Pro_02

0 阅读3分钟

前言

这篇文章介绍了第一个专题——双指针,读者可以从我的Github查看源码以及题目链接和笔记

双指针法

首先,我们需要理解什么是双指针。双指针的应用场景往往是数组,或者说是连续的情况,比如字符串,另一个特征就是涉及到了分块,也就是一块连续的内容被分块解决,还有指针不一定是指针,往往是下标,当然具体我们结合题目分析。

Pro_01 移动零

image.png
写一道算法题我们需要有三个步骤,首先是读题,通过题干不难理解题意,不过本题的难点是规定对数组原地操作而且不能复制,作者下面图文结合给读者进行讲解:

image.png 如图作者将一串数组分成了三个部分,其实这是一个双指针的经典应用,首先本题必须改造数组本身,其次本质上有三个部分:处理的0,处理的非0以及未处理部分,结合这个想法不难想出用两个指针将数组分成三部分,然后我们开始思考两个指针怎么动,其实很简单:

image.png 如图不难看出dest和cur所代表的意义,如果现在的目的是把0放在末尾,那么当cur往后遍历遇到非0时,应该让这个非0元素和0互换位置,这样才能保证0一定在非0元素的后面,如此推理,其实本题就迎刃而解了:
cur从前往后遍历过程中:

  1. 遇到0:cur++
  2. 遇到非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 复写零

image.png
先涂镀:遇到0就写两次,一直到结束,不过需要对数组就地操作,对于这种就地操作的,我们通常先进行异地操作然后把异地操作转到就地操作
然后进行算法思路:首先考虑异地操作,两个指针即可解决,然后考虑就地操作,首先考虑正常情况下的cur和dest,也就是从前往后遍历,但是由于cur碰到0的时候会导致dest向后移动两位,这也就导致了会有值被覆盖,因此这样是错误的,所以我们尝试从后往前走,这时候我们就要考虑cur和dest的位置了:通过给的示例,我们可以反过来看,把cur起始位置定义到复写后的最后一个数,dest直接指向最后一个数就行,然后从后向前进行复写操作即可,通过观察不难得出这样是行得通的,下面归纳一下算法步骤:

  1. 确定复写的最后一个位置
  2. 定义cur,dest
  3. 从后往前进行相关操作
  4. 注意边界条件
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--;
            }
        }
    }
};

结语

下面会继续更新,希望读者持续关注