【算法】——双指针(中)

算法学习:

https://blog.youkuaiyun.com/m0_73953114/category_12866812.html

前言

        在上一节中,相信大家已经初步认识了双指针算法,那么在这一节中,将继续通过具体的题目操练的方式来帮助小伙伴们进一步对双指针的认识。

目录

 1.移动零【力扣】

2.复写零


 1.移动零【力扣】

题目解析:

        对于这道题目,我们需要将系统所给的数组分成两部分左边为非零部分,右边为零部分。对于非零部分中的元素,题目要求他们的相对顺序不能改变

        图示如下:

 

算法原理:

        在知道我们需要将数组分为两部分的时候,非零和零部分,那么我们想做的是不是就是遍历这个数组,一旦发现一个为0的元素,我们就将其与后面的第一个非零元素进行交换,直至将所有的零元素在非零元素的后面

        图示如下: 

​​​​​​

        那么,我们该如何与双指针进行联系起来呢?

        我们的目的是将该数组中的元素划分成非零部分和零部分,那么我们就可以设置两个指针,分别为cur和target。cur指针用来遍历这个数组发现非零元素,而target指针则来帮助我们进行非零元素和零元素的交换实现数组的分区

        通过cur和target这个指针,其实我们在不断进行遍历这个数组的时候就将这个数组分成了三部分,cur将数组分为处理过的和待处理的,而target指针则将处理过的数组划分为非零元素和零元素。

        【0,dest】,【dest+1,cur-1】,【cur,n-1】

          非零元素          零元素                       未处理

                        已处理

        图示如下: 

 

具体代码:

class Solution1 {
public:
    void moveZeroes(vector<int>& nums) {
        int cur = 0, dest = -1;
        size_t n = nums.size();
        for (; cur < n; cur++)
        {
            if (nums[cur]) swap(nums[++dest], nums[cur]);
        }
    }
};

 最后的动画演示效果:

2.复写零【力扣】

题目解析: 

        对于这道题目,当我们遍历这个数组的时候,一旦我们遇到非零元素,就要将其复写一次,也就是在这个零元元素与下一个元素之间插入一个零元素,要注意的一点就是一旦数组 给定,那么其大小是固定不变的。那么最后的0和5这两个元素就会被挤出这个数组

        图示如下:

 

算法原理:

        对于这道题目,我们可以首先考虑异地操作

        对于异地操作来说就很简单,只需要再建立一个与原数组相同大小的数组,在遍历原数组的时候一旦我们遇到零元素就拷贝到新数组两次,而非零元素我们就只需要拷贝一次

         但是题目要求的是同步操作,当我们仿照上面的异地操作进行的时候,就是非零元素不动,遇到零元素,就往后补一个零,但是这样子并不能得到我们想要的数组,会是这样的一个样子,这是由于我们在复写零的时候会将后面的有效元素进行覆盖,从而导致了其丢失

        这时候我们会发现这是由于我们是从前往后写复写导致有效元素的覆盖,那么如果我们从数组的末尾往前来写的时候,是不是当我们进行复写零的时候,有效元素是依旧存在的。但是这解决了复写时有效元素被覆盖的问题,但是紧接着就是我们需要找到复写后数组的最后一个元素,这样我们才知道数组的末尾该从哪里进行复写?

        因此本题的第一个步骤就是先找到最后一个“复写”的数然后就是进行复写的步骤。那么依旧与上一题思路类似,我们通过两个指针cur和dest,cur指针从我们找到的最后一个“复写”的数开始往前遍历,而dest指针则根据cur的情况进行复写:1.cur指向零,复写两次 2.cur不指向0,复写一次 ,直至cur指针遍历到数组的开头为止

        那么到此为止,有关算法思路的讲解就到此结束了,具体代码和图示如下:

 

#复写零

class Solution6 {
public:
    void duplicateZeros(vector<int>& arr) {
        //找到复写后数组的最后一个元素
        int cur = 0,dest = -1;
        int sz = arr.size();
        for (; cur < arr.size(); cur++)
        {
            if (arr[cur] != 0) dest++;
            else dest += 2;
            //判断dest是否已经来到最后一个位置
            if (dest >= arr.size() - 1) break;
        }
        //开始进行复写
        // if(dest>sz-1) dest=sz-1;
        while (cur >= 0)
        {
            if (arr[cur] != 0)
            {
                if (dest > sz - 1) dest--;
                else arr[dest--] = arr[cur];
            }
            else {
                //避免dest越界写    
                if (dest > sz - 1) dest--;
                else arr[dest--] = arr[cur];
                arr[dest--] = arr[cur];
            }
            cur--;
        }
    }
};

评论 153
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_麦麦_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值