力扣27 - 移除元素【双指针】

简介: 如何使用双指针来实现所需元素的移除

力扣原题: 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循环来实现呢,让解题变得高效又快速。接下来,就给小伙伴们讲讲这题如何用双指针来实现:smile:


一、什么是双指针?

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

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

二、双指针思路流程

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

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

②双指针一同偏移

请添加图片描述

③找到待删元素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];                 
         }

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

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;
}

总结

本题的时间复杂度为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


最后很感谢大家的观看,如果有不好的地方请指正,如果觉得可以记得点个赞再走哦:heart:
相关文章
|
1月前
【力扣】-- 移除链表元素
【力扣】-- 移除链表元素
36 1
|
20天前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
30 3
|
1月前
【LeetCode 27】347.前k个高频元素
【LeetCode 27】347.前k个高频元素
34 0
|
19天前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
30 2
|
1月前
【LeetCode 06】203.移除链表元素
【LeetCode 06】203.移除链表元素
30 0
|
1月前
【LeetCode-每日一题】移除元素
【LeetCode-每日一题】移除元素
32 0
|
3月前
|
存储 算法
LeetCode第83题删除排序链表中的重复元素
文章介绍了LeetCode第83题"删除排序链表中的重复元素"的解法,使用双指针技术在原链表上原地删除重复元素,提供了一种时间和空间效率都较高的解决方案。
LeetCode第83题删除排序链表中的重复元素
|
1月前
|
C语言
无头链表二级指针方式实现(C语言描述)
本文介绍了如何在C语言中使用二级指针实现无头链表,并提供了创建节点、插入、删除、查找、销毁链表等操作的函数实现,以及一个示例程序来演示这些操作。
25 0
|
2月前
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。