力扣每日一题(2024.06.20):移除重复元素

113 阅读2分钟

题目:

给你一个 非严格递增排列 的数组 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,太难受了,感觉我这思路没毛病啊,只移动要移动的数,时间复杂度也是O(n)O(n),欢迎大家指正。

最后附上力扣大佬的代码解题思路及代码:

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;
}