🍋1.移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
题目链接:移除元素https://leetcode-cn.com/problems/remove-element/
题目解析:首先大家要清楚这里所谓的移除元素,并不是真的移除,我们数组的内存都是连续存储的,是不能单独删除数组中某个元素的,只能进行覆盖。这里之所以要返回新的新数组长度,是它对原数组只遍历到该长度。所以我们的目的就是让值为val的元素全部被后面的元素覆盖掉。
方法1:暴力遍历
因为目的是要把val全部放到后面,我们可以通过两层for循环,第一层对数组进行遍历,当找到值为val的元素后,我们让val后面的元素全部向前移动一格,这样就将val覆盖掉了。很多人肯定很奇怪,那你这样val不是就没有了?可以答案并不关心你数组尾部的元素是多少,它只会遍历你给的返回值的长度,当然你也可以让val不断和后面元素交换,把它放到后面去,不过那样便是多此一举了。
时间复杂度O(n^2):n为数组的长度,两层for循环,最坏的情况下会对数组完全遍历两次
空间复杂度O(1)
class Solution { public int removeElement(int[] nums, int val) { int n=nums.length; //遍历数组 for(int i=0;i<n;i++){ //找到了val if(nums[i]==val){ //把val后面的元素全部向前移动一格 for(int j=i+1;j<n;j++){ nums[j-1]=nums[j]; } //因为后面的元素都向前移动了一格,i也得往左移动一格(仔细想想) i--; //删除了一个val,长度当然要减1,这也是我们最后需要返回的答案。 n--; } } return n; } }
方法2:快慢双指针
我们定义一个慢指针slowIndex和一个fastIndex,在一个for循环中可以同时完成上面暴力做法的工作。首先两个指针都从数组开始,然后for循环让快指针进行移动,每次移动后判断,快指针的值是否为val,如果不是则将快指针的值覆盖掉慢指针,然后慢指针移动一格。很多人肯定看不懂这个操作的作用,我们的目的既然是主要除了val的其他元素,那我们是不是将除了val的其他元素放到数组前面即可,慢指针的意义代表的是待插入的下标,快指针就是去寻找不为val的元素,理解两个指针的意义再回去看操作大家伙就能明白了。
时间复杂度O(n):n为数组的长度,只遍历了数组一次
空间复杂度O(1)
class Solution { public int removeElement(int[] nums, int val) { //快慢指针 int slowIndex; for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) { //不等于val才是我们需要的元素 if (nums[fastIndex] != val) { //把它放到慢指针的位置 nums[slowIndex] = nums[fastIndex]; //慢指针移动一格 slowIndex++; } } //慢指针最后指向的下标就是我们往前放入元素的个数,也就是我们的答案 return slowIndex; } }
将朴素做法和双指针做法同时写出来和解析,可以更加让我们感受到双指针的魔力所在,帮助我们更好理解。