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

leetcode

 

解题思路

本题要求对非严格递增有序数组进行原地去重,核心在于双指针法的高效实现。通过快指针遍历数组,慢指针标记唯一元素的位置,既保证时间复杂度为 O(n),又满足空间复杂度 O(1) 的要求。以下是具体实现策略:


关键步骤

  1. 边界处理
    若数组为空,直接返回 0;若数组长度为 1,直接保留该元素。

  2. 双指针初始化

    • slow 指针标记唯一元素的插入位置(初始为 1,因为第一个元素无需处理)。
    • fast 指针遍历数组(从索引 1 开始)。
  3. 遍历与更新
    当 fast 指针发现当前元素与前一个不同时,将其复制到 slow 指针位置,并移动 slow 指针。 

  4. 终止条件
    遍历完成后,slow 的值即为去重后的数组长度。

func removeDuplicates(nums []int) int {
    n := len(nums)
    if n == 0 {
        return 0
    }

    slow := 1 // 慢指针从第二个位置开始(第一个元素无需处理)
    for fast := 1; fast < n; fast++ {
        // 当发现新元素时,更新慢指针位置
        if nums[fast] != nums[fast-1] {
            nums[slow] = nums[fast]
            slow++
        }
    }
    return slow
}

复杂度分析

指标说明
时间复杂度 O(n) 单次遍历数组
空间复杂度 O(1) 仅使用两个指针变量

运行示例

func main() {
    // 示例1
    nums1 := []int{1, 1, 2}
    k1 := removeDuplicates(nums1)
    fmt.Println("示例1输出:", k1, nums1[:k1]) // 输出: 2 [1 2]

    // 示例2
    nums2 := []int{0, 0, 1, 1, 1, 2, 2, 3, 3, 4}
    k2 := removeDuplicates(nums2)
    fmt.Println("示例2输出:", k2, nums2[:k2]) // 输出: 5 [0 1 2 3 4]
}

核心逻辑解析

  1. 原地修改原理
    Golang 的切片(Slice)底层是数组的引用,直接修改 nums[slow] 会同步修改原始数组。 

  2. 非严格递增特性
    由于数组已排序,重复元素必然连续出现,因此只需比较相邻元素即可判断是否重复。

  3. 去重动态过程
    以输入 [0,0,1,1,1,2] 为例:

    • fast=1nums[1]=0 与前一个相同,不操作。
    • fast=2nums[2]=1 与前一个不同 → nums[1]=1slow=2
    • fast=5nums[5]=2 与前一个不同 → nums[2]=2slow=3
      最终去重后数组为 [0,1,2],长度为 3。

扩展讨论

若题目允许最多保留两次重复元素(如 LeetCode 80),可扩展为:

    1. 增加计数器 count 记录当前重复次数。
    2. 当 count ≤ 2 时保留元素,否则跳过。

posted @ 2025-03-28 15:28  云隙之间  阅读(59)  评论(0)    收藏  举报