力扣题目:27. 移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/remove-element
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
主要想解释一下代码随想录中的“快慢指针法”
本质其实就是将旧数组中的元素放到新数组中去而已,本质的代码如下
class Solution {public: int removeElement(vector<int>& nums, int val) { // 快慢指针法的本质如下,我觉得叫新旧指针法更好理解 // old_index遍历整个数组 vector<int> new_nums; // 存放结果数组 for (int old_index = 0; old_index < nums.size(); old_index ++ ) { if (nums[old_index] != val) { new_nums.push_back(nums[old_index]); } } // 将新数组的值放回旧数组,这步不用在意,反正已经达不到题目要求的空间复杂的O(1)啦,就是AC一下,测试一下 for (int i = 0; i < new_nums.size(); i ++ ) nums[i] = new_nums[i]; return new_nums.size(); }};
优化上述代码的空间
其实新数组(向量vector类型)new_nums没必要开,因为新数组的指针下标(好吧用了push_back,看不出指针下标了),那我再写个更好理解的代码如下:
class Solution {public: int removeElement(vector<int>& nums, int val) { // 快慢指针法的本质如下,我觉得叫新旧指针法更好理解 // old_index遍历整个数组 int new_nums[110]; // 存放结果数组 int new_index = 0; for (int old_index = 0; old_index < nums.size(); old_index ++ ) { if (nums[old_index] != val) { new_nums[new_index] = nums[old_index]; new_index ++ ; // 新数组的指针 } } // 将新数组的值放回旧数组,这步不用在意,反正已经达不到题目要求的空间复杂的O(1)啦,就是AC一下,测试一下 for (int i = 0; i < new_index; i ++ ) nums[i] = new_nums[i]; return new_index; }};
这时候可以开始说了
注意看上一块代码,new_index指针指向的是结果数组,也就是去掉指定元素后的数组,old_index指针用于遍历旧数组,也就是原始数组。我们会发现,new_index指针移动起来不会比old_index指针快的,也就是new_index <= old_index, 为什么呢?因为old_index指针每查看一个旧数组的元素,就判断这个该不该放到新数组中去,也是查看一个判断一个,new_index最大也就是跟old_index一样大(此时情况:没有一个要删除的元素,旧数组当前已遍历到的元素全部丢到新数组中去)。
综上,new_index既然不会比old_index大,那直接利用原始数组的空间即可,不用浪费空间开新数组了,旧数组已经被遍历过的位置是可以利用的啦。
代码如下:
class Solution {public: int removeElement(vector<int>& nums, int val) { // 快慢指针法的本质如下,我觉得叫新旧指针法更好理解 // old_index遍历整个数组 int new_index = 0; for (int old_index = 0; old_index < nums.size(); old_index ++ ) { if (nums[old_index] != val) { nums[new_index] = nums[old_index]; new_index ++ ; // 新数组的指针 } } return new_index; }};
附:我自己的双指针思路
我的双指针是丢头和尾,头那边的指针从头开始往后走,碰到要删除的,丢到末尾去就行(也就是和尾指针的元素交换位置),并将尾指针前移一位,相比快慢指针法(也就是上一块代码),缺点:改变了元素的的相对位置。
class Solution {public: int removeElement(vector<int>& nums, int val) { int len = nums.size(); // 与val相等的数与末尾数交换位置,并且长度减1 // 1 2 2 3 1 val = 1, 1丢到最后一位,并且长度减1 for (int i = 0; i < len; i ++ ) { if (nums[i] == val) { swap(nums[i], nums[len - 1]); len -- ; // 长度 i -- ; // 保持指针不动 } } return len; }};