一、题目描述
- 给你一个升序排列的数组
nums
,请你原地删除重复出现的元素,使每个元素只出现一次,返回删除后数组的新长度。元素的相对顺序应该保持 一致。 - 由于在某些语言中不能改变数组的长度,所以必须将结果放在数组
nums
的第一部分。更规范地说,如果在删除重复项之后有k
个元素,那么nums
的前k
个元素应该保存最终结果,将最终结果插入nums
的前k
个位置后返回k
。 - 不要使用额外的空间,你必须在原地修改输入数组,并在使用
O(1)
额外空间的条件下完成。
示例:
输入 | 输出 | 解释 |
---|---|---|
nums = [1,1,2] | 2, nums = [1,2,_ ] | 函数应该返回新的长度 2,并且原数组nums的前两个元素被修改为1, 2。不需要考虑数组中超出新长度后面的元素。 |
nums = [0,0,1,1,1,2,2,3,3,4] | 5, nums = [0,1,2,3,4] | 函数应该返回新的长度5,并且原数组nums的前五个元素被修改为0, 1, 2, 3, 4。不需要考虑数组中超出新长度后面的元素。 |
提示:
- 0 < = n u m s . l e n g t h < = 3 ∗ 1 0 4 0 <= nums.length <= 3 * 10^4 0<=nums.length<=3∗104
- 1 0 4 < = n u m s [ i ] < = 1 0 4 10^4 <= nums[i] <= 10^4 104<=nums[i]<=104
- nums 已按升序排列
进阶:
该问题的通用解法?即扩展到数字可重复 k k k 位。
二、求解思路:双指针法
- 由于数组
nums
是有序数组,所有相等元素必定是挨在一起的,利用这一特性我们可以采用双指针的方法解题。 - 定义两个指针
fast
和slow
分别为快指针和慢指针,快指针表示遍历数组到达的下标位置,慢指针表示下一个不同元素要填入的下标位置,初始时两个指针都指向下标值1。 - 快指针遍历整个数组,判断的条件为是否与上一元素相同,不同则将其放入慢指针指向的位置,并将慢指针加一,相同则快指针加一。
- 优化:当
nums
本身就是无重复元素时,该算法会一直进行原地赋值,为了防止原地赋值,我们可以添加一个小判断,当fast - slow > 0
时,才进行赋值。
C++代码
int removeDuplicates(vector<int>& nums) {
int n = nums.size();
if (n == 0) {
return 0;
}
int fast = 1, slow = 1;
while (fast < n) {
if (nums[fast] != nums[fast - 1]) {
if(fast - slow > 0) {
nums[slow] = nums[fast];
}
++slow;
}
++fast;
}
return slow;
}
Python代码
def removeDuplicates(nums: List[int]) -> int:
if not nums:
return 0
n = len(nums)
fast = slow = 1
while fast < n:
if nums[fast] != nums[fast - 1]:
if(fast - slow > 0):
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow
Java代码
public int removeDuplicates(int[] nums) {
int n = nums.length;
if (n == 0) {
return 0;
}
int fast = 1, slow = 1;
while (fast < n) {
if (nums[fast] != nums[fast - 1]) {
if(fast - slow > 0) {
nums[slow] = nums[fast];
}
++slow;
}
++fast;
}
return slow;
}
复杂度分析
- 时间复杂度: O ( n ) O(n) O(n),其中 n 是数组的长度。快指针和慢指针最多各移动 n 次。
- 空间复杂度: O ( 1 ) O(1) O(1),只需要使用常数的额外空间。
三、通解:扩展到数字至多可重复 k 位。
- 遍历初值为
0
,而判断条件idx < k
,目的是将前k
个元素直接复制,此时产生了或短路,因此后面条件无需判断,不会出现idx - k < 0
的情况。
C++代码
int removeDuplicates(vector<int>& nums) {
return process(nums,1);
}
int process(vector<int>& nums,int k){
int idx = 0;
for(auto x : nums){
if(idx < k or nums[idx - k] != x){
nums[idx++] = x;
}
}
return idx;
}
Python代码
def removeDuplicates(self, nums: List[int]) -> int:
def process(nums, k):
idx = 0
for x in nums:
if idx < k or nums[idx-k] != x:
nums[idx] = x
idx += 1
return idx
return process(nums, 1)
Java代码
public int removeDuplicates(int[] nums) {
return process(nums, 1);
}
int process(int[] nums, int k) {
int idx = 0;
for (int x : nums) {
if (idx < k || nums[idx - k] != x) nums[idx++] = x;
}
return idx;
}
复杂度分析
- 时间复杂度: O ( n ) O(n) O(n)。
- 空间复杂度: O ( 1 ) O(1) O(1)。