26. 删除有序数组中的重复项

198 阅读5分钟

【题目】

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

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
  • 返回 k 。

判题标准:

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

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 。不需要考虑数组中超出新长度后面的元素。

提示:

  • 1 <= nums.length <= 3 * 104
  • -104 <= nums[i] <= 104
  • nums 已按 非严格递增 排列

【题目解析】

思路:

解决这个问题的关键在于有效地使用数组索引,同时避免使用额外的存储空间。这可以通过双指针技术实现,特别是慢指针和快指针的方法。具体步骤如下:

  1. 初始化两个指针:一个慢指针 new_length 和一个快指针 i。慢指针指向新数组的下一个唯一元素应该插入的位置,而快指针用于遍历原数组。
  2. 遍历数组:从数组的第二个元素开始(即 i = 1),比较快指针指向的当前元素和慢指针指向的前一个元素。如果它们不相等,表示找到了一个唯一的元素,将其复制到慢指针指向的位置,并将慢指针向前移动一位。
  3. 更新数组长度:重复上述过程,直到快指针遍历完整个数组。此时,慢指针的位置即为新数组的长度。
class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        if not nums:
            return 0

        new_length = 1
        for i in range(1, len(nums)):
            if nums[i] != nums[new_length - 1]:
                nums[new_length] = nums[i]
                new_length += 1

        return new_length

执行:

image.png

【总结】

适用问题类型: 删除有序数组中的重复项问题是一种常见的数组操作问题,特别适用于需要优化空间复杂度的场景。这类问题通常要求在不使用额外空间的情况下,对数组进行原地修改。适用于此方法的问题类型通常包括:

  • 从有序或部分有序的数组中删除重复项。
  • 将数组中满足特定条件的元素移至数组的一端。
  • 原地修改数组以满足某些约束条件,例如将所有零移至数组末尾。
  • 在保持数组元素相对顺序的前提下,进行原地数组变换。

使用的算法: 解决此问题的算法是双指针法,特别是慢指针和快指针的应用。算法的核心在于:

  1. 慢指针new_length):标识新数组的长度,同时指向下一个唯一元素应插入的位置。
  2. 快指针i):用于遍历原数组,寻找唯一的元素。
  3. 原地修改:当快指针指向的元素与慢指针前的元素不同时,将快指针的元素复制到慢指针的位置,然后递增慢指针。

算法细节

  • 遍历数组时,快指针检查每个元素是否与前一个元素不同。
  • 如果元素是唯一的,则将其移动到慢指针指向的位置,并递增慢指针。
  • 这样,数组的前 new_length 个元素都是唯一的,且保持了它们最初的顺序。

算法性能

  • 时间复杂度为 O(n),其中 n 是数组的长度。我们需要遍历一次数组来识别所有唯一的元素。
  • 空间复杂度为 O(1),因为不需要额外的存储空间,所有操作都是在原数组上完成的。

总结与扩展: 删除有序数组中的重复项问题是对双指针技术的一个经典应用,展示了如何有效地在有限空间内操作和修改数据。这种技术在处理数组和类似数据结构时非常有用,特别是在面对空间优化的挑战时。此外,这个问题还教会我们如何在保持数据相对顺序的同时进行原地修改,这对于开发高效算法至关重要。掌握双指针技术对于解决一系列数组和字符串处理问题是非常有帮助的,可以广泛应用于数据分析、图像处理和其他需要高效数据操作的领域。

题目链接

删除有序数组中的重复项