题目:
给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。
考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:
- 更改数组
nums,使nums的前k个元素包含唯一元素,并按照它们最初在nums中出现的顺序排列。nums的其余元素与nums的大小不重要。 - 返回
k。
示例:
输入: nums = [1,1,2]
输出: 2, nums = [1,2,_]
解释: 函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
解析:
实现思路一:通过每次遍历,判断当前位置与之前的数是否相同,如果相同,则将后面所有的数前移一位,然后继续判断; 实现思路二:借助set和偏移数组,set中存放不重复的数,偏移数组存放旧数组每个数要移动的位置(为0的代表不移动,非0代表就数组中对应位置的数要移动后的正确位置),最后将旧数组参照新数组中不为0的数移动。比如原数组[1,1,2],偏移数组[0,0,1],最终数组[1,2,2]. 这种方式能更少的移动数组元素。
此处用思路二实现,代码如下:
public static int removeDuplicates(int[] nums){
// 记录原数组每个位置要偏移的量
int[] offsetArr = new int[nums.length];
// 记录不重复的元素
HashSet<Object> elementSet = new HashSet<>();
int offset=0; //前一个要移动的数的位置
for (int i = 0; i < nums.length; i++) {
// 如果当前位置的数在之前没出现过,则将其移动到合适的位置(即前一个要移动的位置+1)
if(!elementSet.contains(nums[i])){
offsetArr[i]=offset++;
elementSet.add(nums[i]);
}else{ // 如果当前位置的值在之前出现了,则保持不变
offsetArr[i]=offsetArr[i-1];
}
}
// 移动时只需移动偏移量不为0的数
for (int i = 0; i < offsetArr.length; i++) {
if(offsetArr[i]==0){
continue;
}
nums[offsetArr[i]]=nums[i];
}
return elementSet.size();
}
不过尴尬的是,我在力扣运行耗时2ms,大部分人的都是0ms,太难受了,感觉我这思路没毛病啊,只移动要移动的数,时间复杂度也是,欢迎大家指正。
最后附上力扣大佬的代码解题思路及代码:
public static int removeDuplicates(int[] nums){
int slow = 1;
for(int quick = 1;quick<nums.length;quick++){
if(nums[quick]==nums[quick-1]){
continue;
}else{
nums[slow]=nums[quick];
slow++;
}
}
return slow;
}