题目:
给你一个 非严格递增排列 的数组 nums
,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums
中唯一元素的个数。
考虑 nums
的唯一元素的数量为 k
,你需要做以下事情确保你的题解可以被通过:
- 更改数组
nums
,使nums
的前k
个元素包含唯一元素,并按照它们最初在nums
中出现的顺序排列。nums
的其余元素与nums
的大小不重要。 - 返回
k
- 输出与输入格式
- 输入:nums = [0,0,1,1,1,2,2,3,3,4] 输出:5, nums = [0,1,2,3,4]
解题思路:
思路一:我们可以创建一个nums1[1000]={nums[0]}用于存储唯一元素,for循环遍历数组,如果后面的元素和前面的元素不一样,代表找到不一样的重复元素元素的第一个,在把第一个赋值给nums1就的得到重复元素元素的第一个也就是唯一元素,接着count统计一下
int removeDuplicates(int* nums, int numsSize) {
int nums1[10000]={nums[0]};
int count=1;//统计唯一元素个数
int j=1;//新数组的元素下标
for(int i=1;i<numsSize;i++) {
if(nums[i]!=nums[i-1]) {
nums1[j++]=nums[i];//把唯一元素存储在nums1
count++;//赋值成功代表唯一元素个数+1
}
}
//因为题目要求更改nums数组
//所以再把nums1赋值给nums
for(int k=0;k<count;k++) {
nums[k]=nums1[k];
}
return count;
}
时间复杂度O(N),但空间复杂度是O(N),空间浪费太多
方法二:双指针
用i代表不确定元素的位置(意思就是nums[i]不知道是nums[i-1]重复元素还是新元素)
j代表访问的元素下标,for循环内如果当前元素nums[j]不等于前一个元素nums[i-1],代表找到新元素,就把新元素nums[j]放在nums[i]处,此时i++指向下一个元素,j++从该元素位置开始找新元素,重复即可
int removeDuplicates(int* nums, int numsSize) {
int i=1;//不确定元素下标
int count=1;//统计唯一元素个数
for(int j=0;j<numsSize;j++) {
if(nums[j]!=nums[i-1]) {
nums[i++]=nums[j];
count++;
}
}
return count;
}
时间复杂度O(N),空间复杂度是O(1)
那末加大难度
给你一个有序数组 nums
,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
既然不允许额外空间,那末方法一创建数组显然用不了,该怎末做?
思路:上一个题是每一个元素只能出现一次,这个题是每一个元素最多出现2次,那末我们可以仿照方法二来做,只需要改变条件i和j(i j代表意思和上面一样)从第三个元素开始访问,每次nums[j]和nums[i-2]比较,为什么不和nums[i-1]比较,因为题目要求最多出现两次,前面的元素只能是相同的或者不同的
不同
1 2 2 2 4 4 4 num[2]!=nums[1]为假,会向后找到4并赋值给nums[2],明显不合适,还得加一个统计前面重复元素出现的个数,
相同
例如 1 1 2 2 2 2 3 3 3这种重复元素且至少出现2次,i不好控制
int removeDuplicates(int* nums, int numsSize) {
if (numsSize <= 2) {//如果数组长度<=2直接返回,此时数组长度为新长度,返回即可
return numsSize;
}
int count=2;
int i = 2; // 从第3个元素开始处理
for (int j = 2; j < numsSize; j++) {
if (nums[j] != nums[i - 2]) {
nums[i++] = nums[j];
count++;
}
}
return count;
}