面试算法题之移除元素

移除元素

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

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

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

双指针

对于要求原地实现移除,我们可以使用双指针的方式实现。

首先我们定义 lr两个指针,r指针指向当前需要处理的元素,l指针则指向要被覆盖的元素,即新数组的尾部。遍历数组,当r指针指向的元素与val不相等时,则将r指针指向的元素移动到l指针指向的数组下标,并将lr两个指针向后移动一位;否则只移动r指针。

最后,l指针指向的下标就是新数组的尾部,根据题意,直接放回l 即可。

ini 复制代码
```cpp
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int n = nums.size(), l = 0, r = 0;
        for(; r < n; r++) {
            if(nums[r] != val) {
                nums[l] = nums[r];
                l++;
            }
        }
        return l;
    }
};

进一步优化,如果我们要移除的元素恰好在数组的头部,此时需要把每一个元素都左移一位,这样比较费时。根据题意,新数组的元素的排序是可以改更改的。这样我们就可以直接将数组中的最后一个元素移动到数组头部,如此也是满足题目的要求。

cpp 复制代码
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int l = 0, r = nums.size();
        while(l < r) {
            if(nums[l] == val) {
                nums[l] = nums[r - 1];
                r--;
            } else {
                l++;
            }
        }
        return l;
    }
};

删除有序数组中的重复项

给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
  • 返回 k

判题标准:

系统会用下面的代码来测试你的题解:

ini 复制代码
int[] nums = [...]; // 输入数组
int[] expectedNums = [...]; // 长度正确的期望答案

int k = removeDuplicates(nums); // 调用

assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
    assert nums[i] == expectedNums[i];
}

如果所有断言都通过,那么您的题解将被 通过

双指针

这题同样可以使用双指针实现,因为题意说明数组是有序的,即重复的元素一定是相邻的。

我们定义 lr两个指针,r指针指向当前需要处理的元素,l指针则指向要被覆盖的元素,即新数组的尾部。遍历数组,当r指针指向的元素与l指针指向元素不相等时,则将r指针指向的元素移动到l指针指向的数组下一位,并将lr两个指针向后移动一位;否则只移动r指针。最后l+1即是新数组的长度。

cpp 复制代码
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int n = nums.size();
        if(n == 0) return 0;
        int l = 0, r = 0;
        for(; r < n; r++) {
            if(nums[l] != nums[r]) {
                nums[++l] = nums[r];
            }
        }
        return l + 1;
    }
};

思考

  1. 若给出的数组不是有序的呢?该如何实现

删除有序数组中的重复项 II

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以**「引用」**方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

go 复制代码
// **nums** 是以"引用"方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 **该长度范围内** 的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

双指针

跟上一题一样,不同的是相同的元素可以保留两位。

同样可以使用双指针实现,只需要稍微改动一下。

首先,数组长度在2及2以下的都可以满足条件。对于超过2的,我们定义lr两个指针,r指针指向当前需要处理的元素,l指针则指向要被覆盖的元素,即新数组的尾部。遍历数组,当r指针指向的元素与l-2指针指向的元素不相等时(刚好满足保留两位相同数的要求),则将r指针指向的元素移动到l指针指向的数组下标,并将lr两个指针向后移动一位;否则只移动r指针。最后,新数组的长度为l,根据题意放回l即可。

cpp 复制代码
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int n = nums.size();
        if (n <= 2) return n;
        int l = 2, r = 2;
        for(; r < n; r++) {
            if(nums[l - 2] != nums[r]) {
                nums[l++] = nums[r];
            }
        }
        return l;
    }
};
相关推荐
xlp666hub32 分钟前
Linux 设备模型学习笔记(1)
面试·嵌入式
南囝coding2 小时前
CSS终于能做瀑布流了!三行代码搞定,告别JavaScript布局
前端·后端·面试
踏浪无痕2 小时前
Go 的协程是线程吗?别被"轻量级线程"骗了
后端·面试·go
一只叫煤球的猫3 小时前
为什么Java里面,Service 层不直接返回 Result 对象?
java·spring boot·面试
求梦8203 小时前
字节前端面试复盘
面试·职场和发展
C雨后彩虹4 小时前
书籍叠放问题
java·数据结构·算法·华为·面试
码农水水5 小时前
中国电网Java面试被问:流批一体架构的实现和状态管理
java·c语言·开发语言·面试·职场和发展·架构·kafka
程序员清风5 小时前
猿辅导二面:线上出现的OOM是如何排查的?
java·后端·面试
CCPC不拿奖不改名5 小时前
数据处理与分析:pandas基础+面试习题
开发语言·数据结构·python·面试·职场和发展·pandas
小徐不徐说6 小时前
避坑指南:Qt 中 Lambda 表达式崩溃原因与高效使用实践
数据库·c++·qt·面试