题目
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1: 给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 你不需要考虑数组中超出新长度后面的元素。
示例 2: 给定 nums = [0,1,2,2,3,0,4,2], val = 2, 函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。
你不需要考虑数组中超出新长度后面的元素。
思路
数组元素不能仅仅原地删除,删除掉某一值后,需要将此值后面的值拉上来填补空缺。
图解
由题目可知,其实是把=value的值全部移到数组的最后面,而不是value的部分移到数组的前面。
想到用两个指针,一个找到=value的值,一个找到!=value的值,把=value和!=value的值交换,这样就能把=value的值往后拉,!=value的值往前拉。
利用left、right指针。
- left指针向前走,走到=value的地方停下,等待被换到后面去。
- right指针往回走,走到!=value的地方停下,等待着被换到前面去。
如上图,两个指针规定的位置停下。
left指向一个=value的值,right指向一个!=value的值。
这样,只需要把left和right指向的值进行交换,就是把value换到后面,而把不是value的值换到前面去了。
细节
循环条件
while(right >= left)
这里为什么有=号?
按理来说,right停在不是value的地方left停在是value的地方,这两者是永远不会相等的。
这里其实是把一个边界条件考虑进去了:数组只有一个元素。nums.length=1,left=right。
指针移动和left、right交换谁先执行?
while(right >=left) {
var temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
if(nums[right] == val) {
right--;
}
if(nums[left] != val) {
left++;
}
}
一般指针的移动都是放在下面的。
因为如果把指针移动放在后面,指针移动后,就能去判断循环条件 right >= left,如果条件不满足就退出,这样不会出现交换时有指针是负数的情况。
最后返回数组长度
最后跳出循环时,left这一索引值,在最后一个!=value的值后面以个。#刚好是移除后数组的长度。
return right + 1;
完整代码
var removeElement = function(nums, val) {
var left = 0;
var right = nums.length - 1;
while(right >=left) {
var temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
if(nums[right] == val) {
right--;
}
if(nums[left] != val) {
left++;
}
}
return right + 1;
};
var removeElement = function(nums, val) {
// 构思:left、right两个指针
// 题目实际上是要求把value放在右边,不是value的放在左边
// 所以,left遇见了value,停下处理;而right得走到不是value的地方,等待别人来换它
// left、right相遇处就是终止条件 right < left
// 1.边界条件还没想清楚
// 这里所说的边界条件,其实是指nums只有1个或0个元素的情况。
var left = 0;
var right = nums.length - 1;
// 2.这里为什么是> 而不是 >=
while(right > left) {
// 3.交换和指针变换的顺序
if(nums[right] == val) {
right--;
}
if(nums[left] != val) {
left++;
}
var temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
return right + 1;
};