【算法】奇妙双指针移除数组元素

7月5日,今天是我在CSDN创作的第一天,在今后的日子里,希望可以多多努力创作出更多好的文章


力扣原题: link

题目:
题目详情
例子:
请添加图片描述
请添加图片描述

前言

许多小伙伴在解这题的时候,都会直接采用暴力解法,即

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int size=nums.size();
        for(int i=0;i<size;++i)
        {
            if(val==nums[i])     //如果在数组中查找到需要目标元素
            {
                for(int j=i+1;j<size;++j)
                {
                    nums[j-1]=nums[j];      //数组元素前移
                }
                i--;        //因为下标i以后的数值都往后移动了一位,所以i也需移动
                size--;     //数组个数-1
            }
        }
        return size;
    }
};

从双层循环就可以看出这很暴力,但是大家有没有想过如何使用一层for循环来实现呢,让解题变得高效又快速。接下来,就给小伙伴们讲讲这题如何用双指针来实现😄


一、什么是双指针?

双指针即快指针与慢指针,利用双指针可以通过一个for循环来实现两层for循环,即用O(n)代替O(n2)

快指针:用来获取新数组中的元素,即不包含需查找的元素
慢指针:获取新数组中需要更新的位置

二、双指针思路流程

第一次写博客,不知道动画怎么上传,就以图片的形式呈现给大家

①首先双指针均指向首元素
请添加图片描述

②双指针一同偏移

请添加图片描述

③找到待删元素2

请添加图片描述

④快指针偏移,但慢指针不动

请添加图片描述

⑤将此时慢指针所保留的目标元素替换成快指针所指向的新元素的值

请添加图片描述

⑥没有找到目标元素,则双指针移动向后移动

请添加图片描述

在没找到下一个待删元素前, 不停地做替换以及指针偏移操作

请添加图片描述

同上
请添加图片描述

⑨ 最后返回的是慢指针当前所指下标是因为它之前刚好是删除完元素的个数
请添加图片描述

三、代码实现

C++

代码如下(示例):

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int fast;       //快指针 - 获取新数组中的元素
        int slow=0;     //慢指针 - 获取新数组中需要更新的位置
        for(fast=0;fast<nums.size();++fast)     //快指针的遍历
        {
            if(nums[fast]!=val)     //当数组元素与所查元素不相等时,元素更新
            {
                nums[slow]=nums[fast];          //相等于删除操作 - 赋值
                slow++;             //慢指针的移动
            }
        }
        return slow;
    }
};

疑难解析:
①这里是只用到了一层for循环,用于快指针的遍历操作,首先,开始判断第一个元素,快指针所指向的值不等于我们所需要删除的目标元素,则更新新数组,把快指针所获取的值赋给慢指针所在的位置。

②在双指针移动的过程中,一旦找到我们需要删除的目标原则,则不进入这一段if判断

         if(nums[fast]!=val)    
         {
             nums[slow]=nums[fast];       
             slow++;             
         }

在这时只有快指针在移动,而慢指针则停止移动,也就是说只有外层的for循环一直在向后遍历。接着到下一个又找到不同元素,开始赋值操作,也就是说相当于把慢指针所在位置的元素替换成快指针此时指向的元素,然后让慢指针也进行一个偏移,使得慢指针与快指针始终是保持一个下标的距离

③其实这个慢指针的偏移,可以放入数组的赋值中,即

         if(nums[fast]!=val)    
         {
             nums[slow++]=nums[fast];                 
         }

这样是不是看起来更精练了呢 😄

C语言【补充】

  • C语言的这段逻辑是我近阶段写的,整体比较有条理性,代码的可读性也比较好,因此作为补充
  • 也是一样的使用的快慢指针法。src为快指针,用于遍历数组元素,寻找不是的待删元素。若是找到了待删元素,则继续往后移动,知道找到不是待删元素为止,将这个src所在位置的数组给到dst目标数组,也就是在本数组上进行一个覆盖的指针,这个可以看到这里均是一个后置++,因为我们需要的是先赋值之后在后移指针
  • 这个的话看需求了,若是你需要先后移指针再赋值的话就需要前置++
  • 最后的话也是一样,返回这个dst的位置即可,因为更新完的新数组个数就是当前下标所在值
int removeElement(int* nums, int numsSize, int val){
    int src = 0;
    int dst = 0;
    while(src < numsSize)
    {
        if(nums[src] == val)
        {
            src++;
        }
        else
        {
            nums[dst++] = nums[src++];
        }
    }
    return dst;
}
  • 既然这样的话,可以看出src无论如何都要++,那么我们就可以将代码修改成这样,也是一样的逻辑
int removeElement(int* nums, int numsSize, int val){
    int src = 0, dst = 0;
    while(src < numsSize)
    {
        if(nums[src] != val)
        {
            nums[dst++] = nums[src];
        } 
        src++;
    }
    return dst;
}

总结

本题的时间复杂度为O(n),空间复杂度为O(1)。双指针法在对于数组的操作中还是很实用的,不仅是有快慢指针,还有那种左右指针相对遍历,可以确保移动最少的元素,这里就不在赘述,有兴趣的小伙伴可以去了解一下。

其他版本及相似题目

一、Java

class Solution {
    public int removeElement(int[] nums, int val) {

        // 快慢指针
        int fastIndex = 0;
        int slowIndex;
        for (slowIndex = 0; fastIndex < nums.length; fastIndex++) {
            if (nums[fastIndex] != val) {
                nums[slowIndex] = nums[fastIndex];
                slowIndex++;
            }
        }
        return slowIndex;

    }
}

二、JavaScript

var removeElement = (nums, val) => {
    let k = 0;
    for(let i = 0;i < nums.length;i++){
        if(nums[i] != val){
            nums[k++] = nums[i]
        }
    }
    return k;
};

三、Python

class Solution:
 	 def removeElement(cls, nums: List[int], val: int) -> int:
        fast = slow = 0

        while fast < len(nums):

            if nums[fast] != val:
                nums[slow] = nums[fast]
                slow += 1

            # 当 fast 指针遇到要删除的元素时停止赋值
            # slow 指针停止移动, fast 指针继续前进
            fast += 1

        return slow

相关题目推荐
26.删除排序数组中的重复 link

283.移动零 link


最后很感谢大家的观看,如果有不好的地方请指正,如果觉得可以记得点个赞再走哦❤️

  • 12
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

烽起黎明

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

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

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

打赏作者

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

抵扣说明:

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

余额充值