【LeetCode】82. 删除排序链表中的重复元素 II

leetcode

 

算法思路

核心目标:​​删除所有重复元素,只保留唯一值​​。关键点在于:

  1. ​​哑节点预处理​​:避免处理头节点被删除的特殊情况
  2. ​​双指针探测​​:用前驱指针 pre 维护有效链表,当前指针 cur 探测重复区域
  3. ​​重复区间检测​​:当发现 cur 与后续节点重复时,跳过整个重复区间

实现代码

type ListNode struct {
    Val  int
    Next *ListNode
}

func deleteDuplicates(head *ListNode) *ListNode {
    // 创建哑节点,处理头节点可能被删除的情况
    dummy := &ListNode{Next: head}
    pre := dummy
    cur := head

    for cur != nil {
        // 检测重复区域:当前节点与下一节点值相同
        if cur.Next != nil && cur.Val == cur.Next.Val {
            // 找到重复区域末尾
            duplicateVal := cur.Val
            for cur != nil && cur.Val == duplicateVal {
                cur = cur.Next
            }
            // 跳过整个重复区域
            pre.Next = cur
        } else {
            // 非重复节点正常移动指针
            pre = cur
            cur = cur.Next
        }
    }

    return dummy.Next
}

关键逻辑解析

  1. ​​哑节点初始化​​:dummy.Next = head 创建虚拟头节点,统一处理逻辑(包括头节点被删除的情况)
  2. ​​重复区域探测​​:当发现 cur.Val == cur.Next.Val 时进入重复处理逻辑:
    • 记录当前重复值 duplicateVal
    • 循环移动 cur 直到脱离重复区域
    • 通过 pre.Next = cur 直接跳过重复区间
  3. ​​非重复处理​​:当没有重复时,正常推进 pre 和 cur 指针

复杂度分析

  • ​​时间复杂度​​:O(n),链表每个节点最多被访问两次
  • ​​空间复杂度​​:O(1),仅使用固定数量指针变量

测试用例验证

func main() {
    // 示例1:1->2->3->3->4->4->5 → 1->2->5
    n1 := &ListNode{1, &ListNode{2, &ListNode{3, &ListNode{3, &ListNode{4, &ListNode{4, &ListNode{5, nil}}}}}}}
    printList(deleteDuplicates(n1)) // 1->2->5->nil

    // 示例2:1->1->1->2->3 → 2->3
    n2 := &ListNode{1, &ListNode{1, &ListNode{1, &ListNode{2, &ListNode{3, nil}}}}}
    printList(deleteDuplicates(n2)) // 2->3->nil
}

扩展优化

  1. ​​早期终止​​:当剩余节点数不足2个时可提前终止循环(cur.Next == nil 时直接退出)
  2. ​​内存回收​​:在跳过重复节点时,可显式断开引用帮助GC回收(但Go的GC会自动处理)
  3. ​​并发安全​​:若需线程安全操作,需添加互斥锁(但链表操作通常不涉及并发场景)
posted @ 2025-04-17 22:47  云隙之间  阅读(29)  评论(0)    收藏  举报