1.题目描述
给你一个有序数组 nums
,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 int len = removeDuplicates(nums); // 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。 for (int i = 0; i < len; i++) { print(nums[i]); }
示例 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.题目分析
这道题目可以理解成限制空间来实现数组指定去重,常规去重就是遍历+复制,这道题限制了复制的使用,也就是不能将得到的值存入新的数组中,而是直接在当前拿到的数组中进行数组值的更改。既然如此,只要让数组的前几位符合要求并且输出这几位,那么就可以达到题目的要求,所以我们需要至少一个索引来遍历数组,一个索引来记录有效位置,那么这个就会想起来是快慢指针了,快指针遍历数组,通过控制快慢指针的步长来使得每一个数字最多重复为2。通过这种方式,可以在遍历过程中直接修改数组,而不需要额外的存储空间。
3.代码实现
class Solution {
public int removeDuplicates(int[] nums) {
int n = nums.length;
if (n <= 2 ) return n;
int slow = 2,fast = 2;
while(fast < n) {
if(nums[slow-2] == nums[fast]) {
fast++;
} else {
nums[slow] = nums[fast];
fast++;
slow++;
}
}
return slow;
}
}
初始化:
检查数组长度 n 是否小于等于2。如果是,则直接返回 n,因为在这种情况下,数组已经是符合要求的。
初始化慢指针 slow 和快指针 fast 都指向索引2,这是因为允许每个元素最多出现两次,所以前两个元素总是保留的。遍历数组,对于每一个 fast 指向的元素,检查它是否与慢指针前两位的元素相同。这确保了即使当前元素是重复的,也只保留最多两个实例。如果 nums[fast] 不等于 nums[slow – 2],则将 nums[fast] 的值赋给 nums[slow],然后将慢指针 slow 向后移动一位。这样就保证了新数组中每个元素最多出现两次。如果 nums[fast] 等于 nums[slow – 2],则跳过此元素,继续下一个循环迭代。
返回结果:
最终返回慢指针 slow 的值作为新数组的有效长度。如果 slow 超过了原始数组的长度 n,则返回 n,以防止越界错误。
4.总结
这道题主要限制就是限制了空间的使用限制,由于我们只遍历了一次数组,因此时间复杂度为 O(n),其中 n 是数组的长度,原地操作,除了几个额外的变量外,不需要额外的空间,因此空间复杂度为 O(1)。所以快慢指针适合对空间要求高的,同时该算法的效率也很高效,本质上是做了两次遍历,和进行复制操作也是两次遍历。下面为结果:
完结!💯