记录力扣刷题第三题:
本文中包含C++两种方法的题解以及Java和Python的题解
题目描述如下
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
来源:LeetCode
修改后数组是对调用者可见的,因此不能直接统计val出现次数来做差返回。也不能直接删去val,因为数组的存储地址是连续的。因此只能移动后面的元素来填补删除元素的位置。
思路:
比较容易想的思路就是直接双for循环暴力解了吧。第一个for循环遍历数组,找到值为val的元素,第二个for循环把此元素后面的元素向前移动。代码如下:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int size = nums.size();
for(int i = 0;i < size; ++i) {
if(nums[i] == val) {
for(int j = i + 1; j < size; ++j) {
nums[j-1] = nums[j];
}
--size;
--i;
}
}
return size;
}
};
过是当然过了,不过这个时间复杂度为O(n^2),挺拉的,因此应该去找一个时间复杂度更低的方法。参考了一些题解,(由于本人纯小白,在此之前根本不知道双指针法,也没想出来这种方法),这里采用双指针法。
即:一个指针做快指针,一个指针做慢指针,快指针用来遍历数组,当快指针对应的元素不等于val时,就让慢指针追上快指针,不对慢指针的值进行修改。当快指针对应的元素等于val时,就让慢指针慢快指针一步,进行++后,慢指针指向快指针对应的元素,而快指针指向了此元素后面的元素,此时再进行nums[slow++]=nums[fast]的操作就让这个值等于val的元素被其后面的元素覆盖了,在此之后,每个元素都会被其后面的元素覆盖,若又有值为val的元素,则再让慢指针慢快指针一步。当快指针指到末尾时,慢指针指向原数组最后一个元素经过覆盖其他元素的值后所在的位置,即经过覆盖后数组的长度。(注意这里说的指针是模拟的指针,并不是真正用*的指针。)这种方法减少了一个for循环,因此时间复杂度为O(n)。
双指针法常用于线性表的操作。
代码如下:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow = 0;
for(int fast = 0; fast < nums.size(); ++fast) {
if(val != nums[fast]) {
nums[slow] = nums[fast];
++slow;
//如果此前没有指为val的,此步执行追快指针操作,若有,此步执行覆盖操作。
}
}
return slow;
//慢指针最后指向的元素,即为删除值为val元素后的最后一个元素
}
};
下面放上Java和Python用双指针的做法:
Java
class Solution {
public int removeElement(int[] nums, int val) {
int slow = 0;
for(int fast = 0; fast < nums.length; ++fast) {
if(val != nums[fast]) {
nums[slow] = nums[fast];
++slow;
}
}
return slow;
}
}
Python
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
slow = 0
for fast in range(0, len(nums)):
if val != nums[fast]:
nums[slow] = nums[fast]
slow += 1
return slow