leetcode 27. Remove Element 简单
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-element
题目描述
Given an array
numsand a valueval, remove all instances of that value in-place and return the new length.Do not allocate extra space for another array, you must do this by modifying the input array in-place with
O(1)extra memory.The order of elements can be changed. It doesn't matter what you leave beyond the new length.
Clarification:
Confused why the returned value is an integer but your answer is an array?
Note that the input array is passed in by reference, which means a modification to the input array will be known to the caller as well.
Internally you can think of this:
// nums is passed in by reference. (i.e., without making a copy)
int len = removeElement(nums, val);
// any modification to nums in your function would be known by the caller.
// using the length returned by your function, it prints the first len elements.
for (int i = 0; i < len; i++) {
print(nums[i]);
}
Example 1:
Input: nums = [3,2,2,3], val = 3
Output: 2, nums = [2,2]
Explanation: Your function should return length = 2, with the first two elements of nums being 2.
It doesn't matter what you leave beyond the returned length. For example if you return 2 with nums = [2,2,3,3] or nums = [2,2,0,0], your answer will be accepted.
Example 2:
Input: nums = [0,1,2,2,3,0,4,2], val = 2
Output: 5, nums = [0,1,4,0,3]
Explanation: Your function should return length = 5, with the first five elements of nums containing 0, 1, 3, 0, and 4. Note that the order of those five elements can be arbitrary. It doesn't matter what values are set beyond the returned length.
Constraints:
0 <= nums.length <= 1000 <= nums[i] <= 500 <= val <= 100
法一:暴力解法
1. 思路
通过双层for循环,外层遍历数组与val进行比对,比对成功后内层循环将该元素移动到数组尾部(类似于冒泡的方式)。
2. 注意点
3. 代码
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) { //内层循环将val值后移
for (int j = i + 1; j < size; j++) {
nums[j - 1] = nums[j];
}
i--; //由于数组中所有元素在前面的操作中向前移了一位
size--; //排除一个元素
}
}
return size;
}
};
4. 分析
时间复杂度:\(O(n^2)\);
空间复杂度:\(O(1)\)。
法二:双指针法(快慢指针)
双指针,指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的。
像二分法查找中我们就使用了对撞指针;而这里我们使用快慢指针。
快慢指针,是两个指针从同一侧开始遍历数组,将这两个指针分别定义为
快指针(fast)和慢指针(slow),两个指针以不同的策略移动,直到两个指针的值相等(或其他特殊条件)为止。
1. 思路
在该题中,使用i作为快指针,遍历整个数组;而使用k作为慢指针(初始值为0)。
当nums[i]与val值相同时,递增i略过该元素;当不同时,复制该值到nums[k],同时递增两个索引,直到i到数组结尾,最后k的大小即数组的新长度。
2. 注意点
3. 代码
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int k = 0;
for (int i = 0; fastIndex < nums.size(); i++) {
if (val != nums[i]) {
nums[k++] = nums[i];
}
}
return k;
}
};
4. 分析
时间复杂度:\(O(n)\);假设数组原长n个元素,要删去的元素为m个,那么遍历n+(n-m)步,赋值的操作个数为n-m个。当m较小时我们应该考虑下面的方法。
空间复杂度:\(O(1)\)。
法三:双指针法(对撞指针)
1. 思路
虽然使用快慢指针后时间复杂度大大降低了,但是由于题中表示不要求顺序,那么在上述方法中就存在冗余的操作了,比如[1,2,3,4,5] val=1,此时则需要一直遍历到尾才能完成。我们能不能直接将前后操作呢,减少这种状况出现。
对撞指针
- 对撞指针是双指针算法之一。
- 对撞指针从两端向中间迭代数组。一个指针从始端开始,另一个从末端开始。
- 对撞指针的终止条件是两个指针相遇。
循环不变量
我们假设数组长度为len:
[0, left)中不含值为val的元素;(right, len - 1]中均为值为val的元素;
设置变量初始值及循环终止条件
left = 0,保证[0,left)初始为空区间;right = len - 1,保证(right, len - 1]初始为空区间;- 当
left > right时,上述两个区间完整覆盖数组所有元素,且得到left为去除后数组长度。
2. 注意点
由于本题只要求去除后的数组内容,故在(right, len - 1]上,不要求一定要将所有的值为val的元素交换过来,只需将这之中的元素向前覆盖。
3. 代码
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
/**
* [0, left)中不含值为val的元素;
* (right, len - 1]中均为值为val的元素;
*/
int left = 0, right = nums.size() - 1;
while (left <= right) {
if (nums[left] == val) {
nums[left] = nums[right]; // 只要将后一个区间中的元素向前覆盖,不用交换
right--;
}else {
left++;
}
}
return left;
}
};
4. 分析
时间复杂度:\(O(n)\);假设数组原长n个元素,要删去的元素为m个,那么遍历n步,赋值的操作个数为m个。当m较小时推荐采取。
空间复杂度:\(O(1)\)。

浙公网安备 33010602011771号