【LeetCode27.移除元素】——双指针法

27. 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

示例 2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

提示:

  • 0 <= nums.length <= 100
  • 0 <= nums[i] <= 50
  • 0 <= val <= 100

题目链接

思路:

本题是经典的双指针算法题,对于一个数组来说,删除一个元素是比较复杂的,每次删除元素后,若要保留原有的顺序,则需要将后面的元素一个个先前“挪”一格位置,这样操作的话,算法的时间复杂度是较高的。

所以就有了双指针法,分为快慢指针法以及左右指针法,能够实现在一个for循环下完成两个for循环的工作。

两遍循环:

我们可以利用一种最朴素的方法,即暴力地使用两遍for循环进行求解,外层的for循环用于查找所要移除的元素,当寻找到所要移除的元素值后,利用内层的for循环,将数组集体向前移动一位。

 //暴力解法
    int removeElement(vector& nums, int val) {
        int size = nums.size();
        for (int i = 0; i < size; i++) {
            if (nums[i] == val) { // 发现需要移除的元素 
                for (int j = i + 1; j < size; j++) { //将数组集体向前移动一位
                    nums[j - 1] = nums[j];
                }
                i--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
                size--; // 记录此时的数组大小
            }
        }
        return size;
    }

快慢双指针:

设置快慢双指针,分别指向数组,完成各自任务。

快指针:不停止,勇往直前,寻找目标元素

慢指针:用于记录更新新数组的下标位置

class Solution {
public:
    int removeElement(vector& nums, int val) {
        int slow = 0; //初始化慢指针
        for (int fast = 0; fast < nums.size(); fast++) { //快指针对整个数组进行遍历,寻找目标值
            if (val != nums[fast]) { //若不为目标值,更新慢指针,并赋值给新数组
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow;
    }
};

总的来说快慢指针的概念还是挺容易理解的,使用起来也十分简介方便。

左右双指针:

左右双指针的思想与之前所提到的二分查找的思想比较相像,通过在数组的两端设置左指针以及右指针,让两个指针在中间汇合。

在循环过程中,当左指针找到等于val的元素时,左指针会停下来。当右指针找到不等于val的元素时,也会停下来,这时候,右指针所指向的不等于val的元素会赋值给左指针所指向的元素。接着,只要左右指针仍未汇合,他们就会继续朝着中间行走,重复刚刚的操作,直至两者相遇,结束对整个数组的遍历。

class Solution {
public:
   int removeElement(vector& nums, int val) {
        int left = 0; //初始化左指针
        int right = nums.size() - 1; //初始化右指针
        while (left <= right) {
            // 找左边等于val的元素
            while (left <= right && nums[left] != val) { //若不等于目标值,继续遍历
                ++left;
            } //找到等于val值的元素时,结束循环

            // 找右边不等于val的元素
            while (left <= right && nums[right] == val) {
                --right;
            }  //找到不等于val值的元素时,结束循环

            // 将右边不等于val的元素覆盖左边等于val的元素
            if (left < right) {
                nums[left++] = nums[right--];
            }
        }
        return left;   // left指向最终数组末尾的下一个元素
    }
}

题解参考:“代码随想录”https://www.bilibili.com/video/BV12A4y1Z7LP

后续也会坚持更新我的LeetCode刷题笔记,欢迎大家关注我,一起学习。
如果这篇文章对你有帮助,或者你喜欢这篇题解,可以给我点个赞哦。

往期回顾:
LeetCode367.有效的完全平方数
LeetCode69.x的平方根
LeetCode35.搜索插入位置
LeetCode704.二分查找
LeetCode34.在排序数组中寻找元素的第一个和最后一个位置

你可能感兴趣的:(【LeetCode27.移除元素】——双指针法)