26.删除有序数组中的重复项
- 给你一个 升序排列 的数组 n u m s nums nums,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
- 元素的 相对顺序 应该保持 一致 。
- 由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。
- 更规范地说,如果在删除重复项之后有 k k k 个元素,那么 n u m s nums nums 的前 k k k 个元素应该保存最终结果。
- 将最终结果插入 n u m s nums nums 的前 k k k 个位置后返回 k k k。
- 不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O ( 1 ) O(1) O(1) 额外空间的条件下完成
方法一:遍历&删除
由于给定的数组 nums \textit{nums} nums 是有序的,因此对于任意 i < j i<j i<j,如果 nums [ i ] = nums [ j ] \textit{nums}[i]=\textit{nums}[j] nums[i]=nums[j],则对任意 i ≤ k ≤ j i \le k \le j i≤k≤j,必有 nums [ i ] = nums [ k ] = nums [ j ] \textit{nums}[i]=\textit{nums}[k]=\textit{nums}[j] nums[i]=nums[k]=nums[j],即相等的元素在数组中的下标一定是连续的。
在遍历过程中,使用一个变量 temp
来存储新的元素,当遍历过程中,当前位置的元素等于 temp
,则说明该元素重复,删除该元素后继续遍历;若当前位置的元素不等于 temp
,则该元素为新元素,将该元素赋值给 temp
,继续遍历,直到结束。
int removeDuplicates(vector<int>& nums) {
if (nums.size() == 0) {
return 0;
}
else {
int temp = nums[0];
for (int i = 1; i < nums.size(); i++) {
if (nums[i] == temp) {
//将重复的元素删除
nums.erase(nums.begin() + i - 1);
//vector删除中间元素后,该元素后面的元素会前移,需要将i前移一位,来防止漏掉元素
i--;
}
else {
temp = nums[i];
}
}
}
return nums.size();
}
时间复杂度: O ( N 2 ) O(N^2) O(N2)
空间复杂度: O ( 1 ) O(1) O(1)
方法二:快慢指针
设置两个指针 slow
和 fast
,slow
示下一个不同元素要填入的下标位置,fast
表示遍历数组到达的下标位置。让慢指针 slow
走在后面,快指针 fast
走在前面探路,找到一个不重复的元素就告诉 slow
并让 slow
前进一步。这样当 fast
指针遍历完整个数组 nums
后,nums[0..slow]
就是不重复元素
int fast_and_slow(vector<int>& nums) {
int n = nums.size();
if (n == 0) {
return 0;
}
//数组中的第一个元素肯定会被保留,所以遍历从下标1开始
int fast = 1, slow = 1;
while (fast < n) {
//遍历到新元素时,将该元素存储到slow位置,fast和slow都需要后移一位
if (nums[fast] != nums[fast - 1]) {
nums[slow] = nums[fast];
++slow;
}
//fast遍历的元素和前一个元素重复,则只需要fast后移
++fast;
}
return slow;
}
时间复杂度: O ( N ) O(N) O(N)
空间复杂度: O ( 1 ) O(1) O(1)