#83 链表指针 简单

删除排序链表中的重复元素

在 LeetCode 上查看 ↗

🎧 语音讲解

开车或通勤时可听,跟着思路走一遍

速度

📋 题目描述

给定一个已排序的链表的头节点 head。

删除所有重复的元素,使每个元素只出现一次。返回已排序的链表。

示例 1

输入:head = [1,1,2]
输出:[1,2]
两个 1 只保留第一个,接着接到 2。

示例 2

输入:head = [1,1,2,3,3]
输出:[1,2,3]
1、3 各删去多余副本,每个值在结果中恰好出现一次。

💭 模拟答题者思考

1. 暴力:遍历链表把值放进新链表或数组,遇到与上一值相同就跳过——正确,但要额外 O(n) 节点或数组,也没利用「已排序、重复值相邻」。

2. 有序性:相同值必然连成一段。对当前保留节点 curr,只要 curr.next 与 curr 同值,就说明下一个是多余副本,应摘掉。

3. 摘掉方式:不移动 curr,令 curr.next = curr.next.next,可能连续多个相同值,用内层 while curr.next and curr.val == curr.next.val 一直跳。

4. 当 curr.next 为空或值不同,说明 curr 这一段去重完成,curr = curr.next 处理下一段。

5. 与 #82 对比:本题是「每个值保留一份」;#82 是「凡重复过的值整段删除」。本题通常不需要 dummy,因为从不删掉「当前唯一的一份」。

🧠 变量语义(先读这三句再编码)

变量类型语义(三句法)
currListNode*定义:结果链表中「已确认保留」的最后一个节点,初始为 head(空链表则直接返回)
维护:head..curr 上每个值至多出现一次,且与 curr.next 之后的未处理段相连
更新:若后继与 curr.val 相同则只改 curr.next 跳过重复节点;否则 curr = curr.next 前进

⌨️ 落码步骤

1. 若 head 为空,直接返回

2. curr = head,当 curr 非空时循环

3. 当 curr.next 存在且 curr.val == curr.next.val:curr.next = curr.next.next(可连续执行,去掉所有同值后继)

4. 否则 curr = curr.next

5. 返回 head

💻 代码实现

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
        curr = head
        while curr:
            while curr.next and curr.val == curr.next.val:
                curr.next = curr.next.next  # 跳过与 curr 同值的后继
            curr = curr.next
        return head
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode* curr = head;
        while (curr) {
            while (curr->next && curr->val == curr->next->val)
                curr->next = curr->next->next;  // 跳过与 curr 同值的后继
            curr = curr->next;
        }
        return head;
    }
};
// 时间 O(n),空间 O(1)

📈 复杂度分析

时间复杂度 O(n)
空间复杂度 O(1)

⚠️ 常见坑

❗ 每删一个重复就 curr = curr.next:会漏掉连续三个及以上同值(如 [1,1,1]),应在同值时只改 next、不前进 curr。

❗ 误用 #82 的「整段删除」或 dummy + prev:本题保留每个值的第一个出现,单指针改 next 即可。

❗ 空链表未处理:while curr 自然不执行,直接返回 head == nullptr,勿对空指针访问 curr.next。

🔍 必测边界 Case

Case 1:空链表
head = [] → []
Case 2:无重复
head = [1,2,3] → [1,2,3](内层 while 不触发,curr 一路前进)
Case 3:连续多个相同值
head = [1,1,1,2] → [1,2](内层 while 需执行两次摘掉多余 1)
Case 4:单节点
head = [1] → [1]