80. 删除有序数组中的重复项 II
难度中等477
给你一个有序数组 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 。 不需要考虑数组中超出新长度后面的元素。
提示:
1 <= nums.length <= 3 * 104
104 <= nums[i] <= 104
nums
已按升序排列
思路
恶臭的双指针 - 我的题解
这道题目一看就知道, 是**"双指针"题目**, 然而这个双指针的实现方式可能因人而异.
我一开始的实现思路比较笨拙, 只考虑了这道题本身的情况: 用指针i来记录, 用指针j来遍历. 判断第j和 j +1个元素是否相等,
- 若等则将第i和i+1个元素设为第j个元素的值, 然后将i指针移动2位, 并将j移到指向不等于目前元素值的位置上.
- 若不等则将第i个元素设为第j个元素的值. 然后i和j同时向后移动.
class Solution {
public int removeDuplicates(int[] nums) {
int N = nums.length;
int i, j;
i = 0;
j = 0;
while (j < N)
{
if (j + 1 == N){
nums[i] = nums[j];
j++;
i++;
}
else if (nums[j] != nums[j + 1])
{
nums[i] = nums[j];
i ++;
j ++;
}
else
{
nums[i] = nums[i + 1] = nums[j];
while (j < N && nums[j] == nums[i]){
j++;
}
i += 2;
}
}
return i;
}
}
很显然, 上面我的双指针实现方法比较啰嗦, 代码也较长. 接下来, 是最为正规, 也最有意义的题解(因为不仅能解决保留2个元素的本题, 而且还能保留任意元素个数) :
正确的双指针 -
设i指针用于记录元素, j指针用于遍历.
对于要保留n个重复元素的数组而言,
- 若第i-n个元素和第j个元素的值相等, 那么表示对于这个元素已经保留了足够个数n个, 因此只需将j指针向右移一位即可.
- 若不等, 那么表示这个元素距离保留n个数量还不够. 因此将其记录到指针i所对应的空间中, 将i向右移一位. 重复这个过程即可
我都快被自己绕蒙了, 还是留个三叶大佬的题解链接吧 https://www.notion.so/80-II-f3321a2b980a4d6fb07e418c8e083fd2#8e99bf62d33044cf90870a3304f06f2b
以下是我根据上述思路写出的代码
class Solution {
public int removeDuplicates(int[] nums) {
int i, j;
i = j = 2;
while (j < nums.length)
{
if (nums[i - 2] != nums[j])
{
nums[i] = nums[j];
i++;
}
j++;
}
return i;
}
}