Leetcode26——删除有序数组中的重复项(双指针法)


Leetcode26——删除有序数组中的重复项(双指针法)

1. 题目简述

给你一个升序排列的数组 nums ,请你原地 删除重复出现的元素,使每个元素只出现一次 ,返回删除后数组的新长度。元素的相对顺序 应该保持一致 。

由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。

将最终结果插入 nums 的前 k 个位置后返回 k 。

不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

判题标准:

系统会用下面的代码来测试你的题解:

int[] nums = [...]; // 输入数组
int[] expectedNums = [...]; // 长度正确的期望答案

int k = removeDuplicates(nums); // 调用

assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
    assert nums[i] == expectedNums[i];
}

如果所有断言都通过,那么您的题解将被 通过。

示例 1:

输入:nums = [1,1,2]
输出:2, nums = [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]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

提示:

0 <= nums.length <= 3 * 104
-104 <= nums[i] <= 104
nums 已按 升序 排列

(原题链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/)

2. 暴力破解法

最简单的想法应该是使用暴力破解法,即使用两层循环,如遇到下标为i + 1的元素与下标为i的元素相同,则将后面的元素依次覆盖到到前一个位置,否则自增内层变量j。此外,用一个变量来记录数组在覆盖元素后的新长度,即初始值为数组长,每次覆盖该变量减少1。

虽然思路非常简单,但是有一个容易出错的地方:这里内层最好是使用while循环,这与我们覆盖的方式有关。覆盖之后,内层循环变量不能加1!否则会越过一个元素哦!也就是说无法完全消除重复的元素。

class Solution {
    public int removeDuplicates(int[] nums) {
        int newLength = nums.length;
        int j;
        for (int i = 0; i < newLength; i++) {
            j = i + 1;
            while (j < newLength) {
                if (nums[i] == nums[j]) {
                    for (int k = j + 1; k < newLength; k++) {
                        nums[k-1] = nums[k];
                    }
                    newLength--;
                }
                else 
                    j++;
            }
        }
        return newLength;
    }
}

代码分析

这个思路非常直接,但是代价是数组去重时会移动大量的元素,这造成了相当程度上资源的浪费。

3. 双指针法

该如何解决暴力解法的问题呢?可见我们要尽可能降低数组元素移动的次数(也就是数组的覆盖去重操作)。

让我们仔细阅读一下题目,我们只要保证处理后的数组前k个元素是不重复且相对顺序不变的即可,我们可以拿一个整型变量i来表示处理后数组各个位置的数组下标,拿另一个整型变量j来表示目前正在遍历的元素。(免费图床质量太差,有空自己搭一个吧)

qcn5WR.jpg

过程的描述(仅举部分例子):

  1. 从初始状态来看,nums[0]为0,nums[1]为1,两者不同,没有重复,故i++,j从i的下一位继续开始。

  2. num[1] = 2,num[2] = 2,重复!但此时i不要动,检查其他重复元素,直到出现不同元素为止。

    j++,num[3] = 3,不同,故i++,j从i的下一位继续开始。

  3. 重复以上过程,直到j指针到数组末端

class Solution {
    public int removeDuplicates(int[] nums) {
        int i = 0, j = 1;
        while (j < nums.length) {
            if (nums[i] != nums[j]) {
                i++;
                nums[i] = nums[j];
            }
            else 
                j++;
        }
        return i + 1; // 返回的是无重复数组长哦
    }
}

代码分析

由于在运行的过程中,每个元素最多被访问两次,最坏情况时间复杂度为O(n),这是一个非常大的提升了!这也是我自己第一次悟出来双指针法!纪念一下!

相关