算法【数组】(2)移除元素

看到有 数组原地数组可变等关键字,一般都是用双指针扫!

题目要求

  • 给你一个数组 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
复制代码

解题思路

看到数组题目,划重点 原地修改数组空间复杂度不能超过O(1)不考虑超出新长度后元素

  • 原地修改数组:意思是以后的元素所在的位置可以重新赋值为其他元素。
  • 空间复杂度不能超过O(1):不能开辟新的内存空间。
  • 不考虑超出新长度后元素:新长度后面的元素可以随意操作。

所以首先想到的解法是维护两个指针 fastslow,快指针扫描符合条件的元素,慢指针负责对新数组进行赋值和记录数组长度。

即:当快指针扫描到不是目标元素的值,慢指针当前位置的元素重新赋值为快指针扫描到的元素。

看代码【暴力解法】:

class Solution {
    public int removeElement(int[] nums, int val) {
        if (nums == null) {
            return 0;
        }
        // 定义慢指针
        int slow = 0;
        int len = nums.length;
        // 使用快指针遍历数组
        for (int fast = 0; fast < len; fast++) {
            int value = nums[fast];
            if (value != val) {
                // 快指针扫描到了符合插入条件的元素,使用慢指针存储
                nums[slow] = value;
                // 慢指针右移
                ++slow;
            }
        }
        // 返回慢指针,此时新数组的长度就是慢指针所在的位置
        return slow;
    }
}
复制代码

算法优化

看到上述的方法,我们会想,还有更好的方法吗?

对于上述的算法,两个指针同时从左侧向右扫描,但是我们发现,如果对于数组 [1,2,3,4,5,6],目标值 1 的时候,后五个元素并没有任何变化,我们可以想到直接将元素 6 替换 1 得到新的数组,并记录长度为 5

所以上述算法可以优化为:维护两个指针(leftright),一个从左向右判断并记录长度,一个从右向左移动。

使用 left 指针遍历数组,扫描到不符合插入条件(指向元素等于目标值)的,将 right 指向的元素拿过来, right 左移。符合插入条件(指向元素不能与目标值)的 left 右移。一直扫描到两个指针重合。结束遍历,此时 left 的位置就是新数组的长度。

代码如下:

class Solution {
    public int removeElement(int[] nums, int val) {
        if (nums == null) {
            return 0;
        }
        // 定义左指针
        int left = 0;
        // 定义右指针
        int right = nums.length - 1;
        // 遍历数组
        while(left <= right) {
            if (nums[left] == val) {
                // 扫描到不符合条件的,将左指针指向的元素赋值为右指针指向的元素
                nums[left] = nums[right];
                // 右指针左移
                --right;
            } else {
                // 符合插入条件,左指针右移
                ++left;
            }
        }
        // 左指针所在的位置就是新数组的长度
        return left;
    }
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享