需求
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。
假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作:
更改 nums 数组,使 nums 的前 k 个元素包含不等于 val 的元素。nums 的其余元素和 nums 的大小并不重要。
返回 k。
示例 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
快慢指针-版本1
看来上面的需求, 我们分析下: 因为是原地移除元素, 所以我们不可能去开辟新的数组. 只能在原数组上操作.
我们可以定义两个连个指针, 一个快指针从头遍历数组, 另一个慢指针最开始放在数组头, 然后去判断快指针指向的值是不是满足条件(因为是要移除等于目标值的元素, 所以这里的满足条件的元素就 是不等于目标值的元素, 即: nums[i] != val), 满足条件后就将这个值放入慢指针所指向的位置,并且将慢指针+1; 循环完所有数组元素后, 返回慢指针即可;
经过分析我们很快的写出了如下代码:
public int removeElement(int[] nums, int val) {
int length = nums.length;
int left = 0;
for( int right = 0; right < length; right++ ){
if( nums[right] != val ){
nums[left++] = nums[right];
}
}
return left;
}
执行效果也很爽.
快慢指针-版本2
版本1 我们已经实现了需求, 并且通过了所有的测试用例.
但是我们再回头分析一下提示内容: 0 <= nums.length <= 100
, 也就是说, 数组的长度有可能是0的, 那么我们的代码就有了优化的空间, 如果数组的长度是0的话, 那么直接返回0即可.
代码如下所示: 其实就是比版本1多了个判断而已.
毋庸置疑, 上面的代码也是通过了所有的测试用例.
版本3 - 双指针双向奔赴
前面的连个版本都是 定义了两个指针, 然后从头开始 一快一慢 的遍历数组.
接下来我们可以定义两个指针, 分别从数组的头和尾遍历, 直到他们碰头结束. 就向现实生活中的一对恋人双向奔赴一样, 充满浪漫的气息~~~
这里要注意的是与之前的快慢指针相比, 逻辑有了小小的变化, 之前的是快指针去比对数据是否满足条件, 但是换成双向奔赴后需要在右边的指针判断的基础上, 左边的指针也判断下, 因为如果左边的指针不判断, 那么到结束条件的时候, 左边的数据就没有参与比较而产生错误奥~
public int removeElement(int[] nums, int val) {
int length = nums.length;
if( length == 0 ){
return 0;
}
int left = 0;
int right = length - 1;
while( left <= right ){
if( nums[left] != val ){
left++;
continue;
}
if( nums[right] != val ){
nums[left++] = nums[right];
}
right--;
}
return left;
}
结尾
以上 是我对移除元素算法的一些遐想和延伸, 可能不是最优解, 但是算法的优化嘛 本身就是一个思索的过程, 能在这个思索和迭代的过程中有所收获和乐趣就是在成长了, 欢迎大家一起来交流更多的解答…