27. 移除元素
题目:https://leetcode-cn.com/problems/remove-element/
自己写的代码(C++)
1 C++: 2 3 class Solution { 4 public: 5 int removeElement(vector<int>& nums, int val) { 6 int len = 0; 7 int i = 0, j = nums.size() - 1; 8 while ( i <= j ){ 9 if (nums[i] == val && nums[j] != val){ 10 int tmp = nums[i]; 11 nums[i] = nums[j]; 12 nums[j] = tmp; 13 len++; 14 i++; 15 j--; 16 } 17 if (nums[i] != val) i++; 18 if (nums[j] == val){ 19 len++; 20 j--; 21 } 22 } 23 return nums.size() - len; 24 } 25 };
题目挺简单的,这是第一次使用C++STL中的容器vector。
1、C++ vector的初始化: https://blog.csdn.net/qq_40147449/article/details/87892312?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link
2、vector作为参数的三种传参方式:https://www.cnblogs.com/xiaoxi666/p/6843211.html
c++中常用的vector容器作为参数时,有三种传参方式,分别如下(为说明问题,用二维vector):
- function1(std::vector<std::vector<int> > vec),传值
- function2(std::vector<std::vector<int> >& vec),传引用
- function3(std::vector<std::vector<int> >* vec),传指针
注意,三种方式分别有对应的const形式,不在此讨论。
三种方式对应的调用形式分别为:
- function1(vec),传入值
- function2(vec),传入引用
- function3(&vec),传入地址
三种方式的效果分别为:
- 会发生拷贝构造
- 不会发生拷贝构造
- 不会发生拷贝构造
自己写的代码(JAVA)
1 JAVA: 2 3 class Solution { 4 public int removeElement(int[] nums, int val) { 5 int len = 0; 6 int i = 0, j = nums.length - 1; 7 while ( i <= j ){ 8 if (nums[i] == val && nums[j] != val){ 9 int tmp = nums[i]; 10 nums[i] = nums[j]; 11 nums[j] = tmp; 12 len++; 13 i++; 14 j--; 15 } 16 else if (nums[i] != val) i++; 17 else if (nums[j] == val){ 18 len++; 19 j--; 20 } 21 22 } 23 return nums.length-len; 24 } 25 }
官方:https://leetcode-cn.com/problems/remove-element/solution/yi-chu-yuan-su-by-leetcode-solution-svxi/
方法一:双指针
由于题目要求删除数组中等于 val 的元素,因此输出数组的长度一定小于等于输入数组的长度,我们可以把输出的数组直接写在输入数组上。可以使用双指针:右指针 right 指向当前将要处理的元素,左指针left 指向下一个将要赋值的位置。
如果右指针指向的元素不等于 val,它一定是输出数组的一个元素,我们就将右指针指向的元素复制到左指针位置,然后将左右指针同时右移;
如果右指针指向的元素等于 val,它不能在输出数组里,此时左指针不动,右指针右移一位。
整个过程保持不变的性质是:区间 [0,left) 中的元素都不等于 val。当左右指针遍历完输入数组以后,left 的值就是输出数组的长度。
这样的算法在最坏情况下(输入数组中没有元素等于val),左右指针各遍历了数组一次。
1 C++: 2 3 class Solution { 4 public: 5 int removeElement(vector<int>& nums, int val) { 6 int n = nums.size(); 7 int left = 0; 8 for (int right = 0; right < n; right++) { 9 if (nums[right] != val) { 10 nums[left] = nums[right]; 11 left++; 12 } 13 } 14 return left; 15 } 16 };
方法二:双指针优化(和我的方法差不多)
如果要移除的元素恰好在数组的开头,例如序列 [1,2,3,4,5],当 val 为 1 时,我们需要把每一个元素都左移一位。注意到题目中说:「元素的顺序可以改变」。实际上我们可以直接将最后一个元素 5 移动到序列开头,取代元素 1,得到序列 [5,2,3,4],同样满足题目要求。这个优化在序列中 val 元素的数量较少时非常有效。
实现方面,我们依然使用双指针,两个指针初始时分别位于数组的首尾,向中间移动遍历该序列。
算法
如果左指针left 指向的元素等于 val,此时将右指针 right 指向的元素复制到左指针 left 的位置,然后右指针right 左移一位。如果赋值过来的元素恰好也等于 val,可以继续把右指针 right 指向的元素的值赋值过来(左指针left 指向的等于 val 的元素的位置继续被覆盖),直到左指针指向的元素的值不等于 val 为止。
当左指针 left 和右指针 right 重合的时候,左右指针遍历完数组中所有的元素。
这样的方法两个指针在最坏的情况下合起来只遍历了数组一次。与方法一不同的是,方法二避免了需要保留的元素的重复赋值操作。
1 C++: 2 3 class Solution { 4 public: 5 int removeElement(vector<int>& nums, int val) { 6 int left = 0, right = nums.size(); 7 while (left < right) { 8 if (nums[left] == val) { 9 nums[left] = nums[right - 1]; 10 right--; 11 } else { 12 left++; 13 } 14 } 15 return left; 16 } 17 };
代码随想录:https://programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html#%E6%80%9D%E8%B7%AF
该方法与官网的方法一差不多。