「这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战」
前言
力扣第八十题 删除有序数组中的重复项 II
如下所示:
给你一个有序数组 nums
,请你 原地 删除重复出现的元素,使每个元素 最多出现两次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以**「引用」**方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
示例 1:
输入: nums = [1,1,1,2,2,3]
输出: 5, nums = [1,1,2,2,3]
解释: 函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。 不需要考虑数组中超出新长度后面的元素。
示例 2:
输入: nums = [0,0,1,1,1,1,2,3,3]
输出: 7, nums = [0,0,1,1,2,3,3]
解释: 函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3 。 不需要考虑数组中超出新长度后面的元素。
一、思路
这一题是要将有序数组中超过两个的元素都删除掉,且必须原地修改
这很自然的就可以想到使用滑动窗口来解决这类计数问题。
我们滑动窗口的左边界指向连续元素的左边,右边界指向连续元素的右边。 这样当某个窗口中的元素个数大于 2
时,就将之后的所有元素都向前移动即可。
大致的步骤如下所示:
- 从左至右遍历数组
- 当
nums[i] == nums[left]
时,让窗口的右边界right++
- 当
nums[i] != nums[left]
时,且窗口中元素大于2
个,right -left > 2
,则计算出移动的距离move += right-left-2
- 在遍历的过程中,只要
move > 0
,就将该元素前移,nums[right-move] = nums[right]
- 遍历结束后,如果最后一个窗口中的元素是否大于
2
,则更新移动距离move += right-left-2
(因为最后一个连续窗口的大小可能大于2
) - 返回新数组长度
len - move
作为结果
举个例子
此处以 nums = {0,0,1,1,1,1,2,2}
作为例子
初始值:
left = right = 0
,move = 0
- 开始遍历数组
nums
- 当
right = 2
时,出现了nums[2] != nums[0]
,但此时窗口中元素不大于2
。故只需将left = right
- 当
right = 6
时,出现了nums[6] != nums[2]
,此时窗口中元素大于2
,计算出移动距离move = 2
,再将left = right
- 因
move > 0
,后续的遍历都需要将元素前移 - 最终数组更新为
{0,0,1,1,2,2,2,2}
,返回新的长度len - move = 8 -2 =6
即可
二、实现
实现代码
实现代码与思路保持一致
public int removeDuplicates(int[] nums) {
int len = nums.length;
if (len < 3){ // 特殊情况
return len;
}
int left = 0;
int right = 0;
int move = 0;
while (right < len) {
if (nums[left] != nums[right]) {
// 判断当前窗口长度是否超过2
if (right - left > 2){
move += right-left-2;
}
left = right;
}
// 元素前移
if (move > 0) {
nums[right-move] = nums[right];
}
right++;
}
// 可能会出现最后的字符未处理的情况
if (right - left > 2) {
move += right-left-2;
}
return len - move;
}
测试代码
public static void main(String[] args) {
int[] nums = {0,0,1,1,1,1,2,2};
new Number80().removeDuplicates(nums);
}
结果
三、总结
感谢看到最后,非常荣幸能够帮助到你~♥
如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~