美文网首页Java
删除排序数组中重复元素的算法

删除排序数组中重复元素的算法

作者: 冬天里的懒喵 | 来源:发表于2020-07-09 15:51 被阅读0次

    [toc]
    在上一篇文章中讨论了关于如何删除排序链表中重复元素的方法。那么如果底层数据结构是数组又将如何处理呢?

    1.删除重复元素,所有元素只保留一次

    可以查看leetcode上的26题:

    给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
    不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
     
    示例 1:
    给定数组 nums = [1,1,2], 
    函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 
    你不需要考虑数组中超出新长度后面的元素。
    示例 2:
    给定 nums = [0,0,1,1,1,2,2,3,3,4],
    函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
    你不需要考虑数组中超出新长度后面的元素。
     
    说明:
    为什么返回数值是整数,但输出的答案是数组呢?
    请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
    你可以想象内部操作如下:
    // nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
    int len = removeDuplicates(nums);
    // 在函数里修改输入数组对于调用者是可见的。
    // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
    for (int i = 0; i < len; i++) {
        print(nums[i]);
    }
    链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array
    

    需要注意的是,如果仅仅是求不重复元素的长度,那么非常简单,计数器一次遍历就能得到结果。但是,本题要求不仅返回长度,如果长度是n,那么前n项恰好就是去重后的数组。这一点就非常关键了。另外,数组要求额外的空间复杂度不超过 O(1)。
    那么面对此问题如何处理呢?
    实际上我们需要想到的是,数组的特性。就是可以利用数组下标对数组中的元素进行随机访问。另外,对于本题中的输入数组,除了长度n要求的前n项是有效的之外,n之后的元素项实际上没有什么意义。
    此时,不难联想到之前解决链表重复的三指针法。在此处我们也可以巧妙的利用两个指针来解析:
    指针分别为 i j 以数组 [0,0,1,1,1,2,2,3,3,4]为例,其解析过程:


    image.png image.png image.png image.png image.png image.png image.png image.png image.png

    遍历完成之后:


    image.png

    代码如下:

        public int removeDuplicates(int[] nums) {
            if (nums.length == 0){
                return 0;
            }
            int i = 0;
            for (int j = 1; j < nums.length; j++) {
                if (nums[j] != nums[i]) {
                    i++;
                    nums[i] = nums[j];
                }
            }
            return i + 1;
        }
    

    代码非常简洁,定义了两个指针,i和j。i表示去重之后的数组的最后一项。则用j反复与i比较。i与j中的差值则是重复的项,在下一次遍历过程中将被新的值替换。
    提交后效果如下:


    image.png

    2.重复元素保留不超过2次

    题目描述:

    给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
    不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
    示例 1:
    给定 nums = [1,1,1,2,2,3],
    函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。
    你不需要考虑数组中超出新长度后面的元素。
    示例 2:
    给定 nums = [0,0,1,1,1,1,2,3,3],
    函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3 。
    你不需要考虑数组中超出新长度后面的元素。
    说明:
    为什么返回数值是整数,但输出的答案是数组呢?
    请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
    你可以想象内部操作如下:
    // nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
    int len = removeDuplicates(nums);
    // 在函数里修改输入数组对于调用者是可见的。
    // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
    for (int i = 0; i < len; i++) {
        print(nums[i]);
    }
    链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array-ii
    

    这又是一种变体,那么我们只需要在第一个问题的解法中加入一个count计数器,所有元素的次数默认都是1次。那么当count小于2的时候,即使所需要的元素,那么其后一项就可以和j交换。反之,这只把j增加。算法如下:

        public int removeDuplicates(int[] nums) {
            if (nums.length == 0){
                return 0;
            }
            int i=0;
            int count = 1;
            for(int j=1;j<nums.length;j++){
                if(nums[i] == nums[j]){
                    count ++;
                }else {
                    count = 1;
                }
                if(count<=2){
                    i ++;
                    nums[i] = nums[j];
                }
            }
            return i+1;
        }
    

    提交后如下:


    image.png

    相关文章

      网友评论

        本文标题:删除排序数组中重复元素的算法

        本文链接:https://www.haomeiwen.com/subject/mhrscktx.html