给你一个数组 nums
和一个值 val
,你需要 原地 移除所有数值等于 val
的元素。元素的顺序可能发生改变。然后返回 nums
中与 val
不同的元素的数量。
假设 nums
中不等于 val
的元素数量为 k
,要通过此题,您需要执行以下操作:
- 更改
nums
数组,使nums
的前k
个元素包含不等于val
的元素。nums
的其余元素和nums
的大小并不重要。 - 返回
k
。
用户评测:
评测机将使用以下代码测试您的解决方案:
int[] nums = [...]; // 输入数组
int val = ...; // 要移除的值
int[] expectedNums = [...]; // 长度正确的预期答案。
// 它以不等于 val 的值排序。
int k = removeElement(nums, val); // 调用你的实现
assert k == expectedNums.length;
sort(nums, 0, k); // 排序 nums 的前 k 个元素
for (int i = 0; i < actualLength; i++) {
assert nums[i] == expectedNums[i];
}
如果所有的断言都通过,你的解决方案将会 通过。
示例 1:
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2,_,_]
解释:你的函数函数应该返回 k = 2, 并且 nums 中的前两个元素均为 2。
你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。
示例 2:输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3,_,_,_]
解释:你的函数应该返回 k = 5,并且 nums 中的前五个元素为 0,0,1,3,4。
注意这五个元素可以任意顺序返回。
你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。
提示:
0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100
- The problem statement clearly asks us to modify the array in-place and it also says that the element beyond the new length of the array can be anything. Given an element, we need to remove all the occurrences of it from the array. We don't technically need to remove that element per-say, right?
- We can move all the occurrences of this element to the end of the array. Use two pointers!
- Yet another direction of thought is to consider the elements to be removed as non-existent. In a single pass, if we keep copying the visible elements in-place, that should also solve this problem for us.
题解思路分析:题目最后只要求输出数组的前k位是符合要求的即可,不要求排序,我首先想到可以使用双指针法,左指针遍历到等于val的元素时,右指针再开始遍历出一个不等于val的元素,然后交互这两个元素的位置。这样就实现了把要移除的元素全挪到右边去,最后左边剩下的元素就是满足要求的了,而且时间复杂度也挺好,遍历一次数组就可以结束,只有O(n)。
题解如下:
class Solution {
public int removeElement(int[] nums, int val) {
int count = 0;
int j = nums.length - 1;
for (int i = 0; i <= j; i++) {
if (nums[i] == val) {
count++;
for (; j > i; j--) {
if (nums[j] == val) {
count++;
} else {
nums[i] = nums[j];
j--;
break;
}
}
}
}
return nums.length - count;
}
}
结果还挺好的,接下来学习下官方的解题思路有没有啥不同。
官方提供了两种题解,一种跟我的解法基本相似,另外一种也是双指针法,但是两个指针都是从头往后遍历的。这种解法是采用从头插入元素的方式实现,其中一个快指针从前往后遍历整个数组,如果当前元素为不等于val的,就把该元素插入数组的头部然后慢指针再自增。
贴一下官方的解法:
class Solution {
public int removeElement(int[] nums, int val) {
int n = nums.length;
int left = 0;
for (int right = 0; right < n; right++) {
if (nums[right] != val) {
nums[left] = nums[right];
left++;
}
}
return left;
}
}
作者:力扣官方题解
链接:https://leetcode.cn/problems/remove-element/solutions/730203/yi-chu-yuan-su-by-leetcode-solution-svxi/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
浏览了下评论区,了解到了双指针算法可以分为2种类型的。
我的解法里的一个指针从前往后遍历的,另一个指针从后往前遍历的,两个指针相遇则结束循环,这种类型叫对撞指针。
官方解法里的,两个指针都是从前往后遍历,但是一个指针每个元素都会遍历到跑的比较快,另一个指针只有满足某些条件才会往下走跑的比较慢,这种类型叫快慢指针,非常的形象。