一,题目
给你一个 非严格递增排列 的数组 nums
,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums
中唯一元素的个数。
考虑 nums
的唯一元素的数量为 k
,你需要做以下事情确保你的题解可以被通过:
- 更改数组
nums
,使nums
的前k
个元素包含唯一元素,并按照它们最初在nums
中出现的顺序排列。nums
的其余元素与nums
的大小不重要。 - 返回
k
。
二,思路
-
理解问题:
- 目标是从一个非严格递增排列的数组
nums
中删除重复的元素,使每个元素只出现一次,并保持原有的相对顺序。 - 最后需要返回新数组中唯一元素的个数
k
。
- 目标是从一个非严格递增排列的数组
-
原地修改:
- 要求在原数组上进行修改,不允许使用额外的数组来存储结果。
- 这样可以节省空间,符合题目的要求。
-
使用双指针:
- 一个指针(
i
)用于遍历整个数组。 - 另一个指针(
k
)用于记录唯一元素在数组中的位置。
- 一个指针(
-
遍历数组:
- 遍历整个
nums
数组,检查当前元素nums[i]
是否与前一个元素nums[k-1]
相同。 - 如果不同,说明是一个新的唯一元素,将其放在
nums[k]
的位置,并增加k
。
- 遍历整个
-
返回结果:
- 遍历结束后,
k
的值就是数组中唯一元素的数量,同时前k
个元素就是去重后的结果。
- 遍历结束后,
三,复杂度分析
时间复杂度
-
遍历数组:
- 函数中使用了一个
for
循环来遍历整个数组nums
。这个循环从索引1
到n-1
(其中n
是数组的长度),因此时间复杂度为 O(n),因为每个元素都被访问了一次。
- 函数中使用了一个
-
条件判断和赋值:
- 在循环内部,对于每个元素,我们进行常数时间的条件判断和赋值操作(将新找到的唯一元素放在
nums[k]
的位置)。这些操作的时间复杂度都是 O(1)。
- 在循环内部,对于每个元素,我们进行常数时间的条件判断和赋值操作(将新找到的唯一元素放在
总体时间复杂度
- 因此,整体的时间复杂度为 O(n),这是处理这个问题时的最优复杂度,因为我们必须遍历整个数组。
空间复杂度
-
原地修改:
- 这个实现是原地的,意味着我们没有使用额外的数组来存储结果,而是直接在
nums
数组上进行修改。
- 这个实现是原地的,意味着我们没有使用额外的数组来存储结果,而是直接在
-
常数空间:
- 只使用了几个额外的变量(如
k
和循环变量i
),这些都占用常数空间,不依赖于输入数据的大小。
- 只使用了几个额外的变量(如
总体空间复杂度
- 因此,空间复杂度为 O(1),表示只使用了常数级别的额外空间。
总结
- 时间复杂度: O(n) — 遍历整个数组的时间。
- 空间复杂度: O(1) — 只使用了常数级别的额外空间。
代码:
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int k=1;//记录不重复元素的个数
for(int i=0;i<nums.size();i++){
if(nums[i]!=nums[k-1]){
nums[k]=nums[i];
k++;
}
}
return k;
}
};
四,子题:力扣80
题目描述:
给你一个有序数组 nums
,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
实现:
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if(nums.size()==2)
return 2;
if(nums.size()==1)
return 1;
int k=2;
for(int i=2;i<nums.size();i++){
if(nums[i]!=nums[k-2]){
nums[k]=nums[i];
k++;
}
}
return k;
}
};