题目
【删除有序数组中的重复元素】
Given a sorted array nums, remove the duplicates in-place such that each element appear only once and return the new length.
Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory.
Example 1:
Given nums = [1,1,2],
Your function should return length = 2
, with the first two elements of nums being 1 and 2 respectively.
It doesn’t matter what you leave beyond the returned length.
Example 2:
Given nums = [0,0,1,1,1,2,2,3,3,4],
Your function should return length = 5
, with the first five elements of nums being modified to 0, 1, 2, 3, and 4 respectively.
It doesn’t matter what values are set beyond the returned length.
Clarification:
Confused why the returned value is an integer but your answer is an array?
Note that the input array is passed in by reference, which means modification to the input array will be known to the caller as well.
Internally you can think of this:
// nums is passed in by reference. (i.e., without making a copy)
int len = removeDuplicates(nums);
// any modification to nums in your function would be known by the caller.
// using the length returned by your function, it prints the first len elements.
for (int i = 0; i < len; i++) {
print(nums[i]);
}
思路1
通常情况下删除元素意味着需要将该位置后面的所有元素进行前移,但是这样是比较耗费时间的,时间复杂度将达到O(n^2)。
本题的特殊性在于,修改后的数组是根据返回的int len
来截取的,也就是说,我们可以不通过移动元素来实现删除,我们只需要把应保留的元素覆盖之前“重复”元素即可。
以[0,0,1,1,1,2,2,3,3,4]
为例:
索引i首先移动到第二个0处,判断出后面没有更多的0了,此时变量dupNum为1,表示数组中有1个多余元素,那么就可以将第一个“1”覆盖掉第二个0(前移dupNum个单位),以达到“删除”的效果,此时的数组是这样的:[0,1,1,1,1,2,2,3,3,4]
接下来i继续移动,直到停留在nums[4]的1处,统计到有2个重复的1,此时变量dupNum为1+2=3,表示后面的2应该往前“移动”3位,此时的数组是这样的:
[0,1,2,1,1,2,2,3,3,4]
…
重复以上步骤,数组最后看起来是这样的:[0,1,2,3,4,2,2,3,3,4]
在上面过程中不断统计新的数组长度:5
那么截取后,新的数组为`[0,1,2,3,4]
时间复杂度O(n)
代码1
class Solution {
public int removeDuplicates(int[] nums) {
if(nums.length==0)
return 0;
int dupNum = 0;//数组中“多余”元素的数量
int newLength = 1;//保存修改后数组的长度
int i = 0;
while(i < nums.length-1){
while(nums[i] == nums[i+1]){//后边的元素与当前元素相同,则i后移直到停留在不同的元素之前
dupNum++; //说明后边的元素是多余的
i++;
if(i == nums.length-1)//i到达数组的末尾则直接返回,避免越界
return newLength;
}
nums[i+1-dupNum] = nums[i+1]; //关键,向前替换掉对应位置的元素,而无需整体移动
newLength++;
i++;
}
return newLength;
}
}
提交结果
Runtime: 5 ms, faster than 99.86% of Java online submissions for Remove Duplicates from Sorted Array.
Memory Usage: 40.3 MB, less than 87.27% of Java online submissions for Remove Duplicates from Sorted Array.
思路2
对上面的思路上进行改进,提高代码的阅读性,采用经典的“双指针法”,j
指针负责遍历数组,i
指针负责标志哪个位置上的元素应该被覆盖(相当于上面代码中的dupNum),因为被覆盖的位置是连续的,所以i
每次自增1。
代码2
class Solution {
public int removeDuplicates(int[] nums) {
if (nums.length == 0) return 0;
int i = 0;
for (int j = 1; j < nums.length; j++) {
if (nums[j] != nums[i]) {
i++;
nums[i] = nums[j];
}
}
return i + 1;
}
}
提交结果
Runtime: 5 ms, faster than 99.86% of Java online submissions for Remove Duplicates from Sorted Array.
Memory Usage: 41.4 MB, less than 66.99% of Java online submissions for Remove Duplicates from Sorted Array.