力扣 83: 删除排序链表中的重复元素(Java实现)

问题描述

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

示例 :

复制代码
输入:head = [1,1,2,3,3]
输出:[1,2,3]

问题分析

这是一个经典的链表操作问题。由于链表已经排序,所有重复的元素都会相邻出现。我们需要遍历链表,当遇到重复元素时,跳过重复的节点。

关键点:

  1. 链表已排序,重复元素必然相邻

  2. 只需要保留每个元素的第一个出现

  3. 需要修改指针来跳过重复节点

解决方案

方法一:直接遍历法

这是最直观的解法。我们使用一个指针遍历链表,比较当前节点和下一个节点的值:

java 复制代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        // 如果链表为空或只有一个节点,直接返回
        if (head == null || head.next == null) {
            return head;
        }
        ListNode cur = head;
        while (cur != null && cur.next != null) {
            if (cur.val == cur.next.val) {
                // 跳过重复节点
                cur.next = cur.next.next;
            } else {
                // 移动到下一个不重复的节点
                cur = cur.next;
            }
        }
        return head;
    }
}

代码解析

直接遍历法详细解析

  1. 边界条件处理:

    java

    复制代码
    if (head == null || head.next == null) {
        return head;
    }

    空链表或只有一个节点的链表不需要去重。

  2. 遍历链表:

    java

    复制代码
    ListNode cur = head;
    while (cur != null && cur.next != null) {
        // 处理逻辑
    }

    使用 cur 指针遍历链表,需要同时检查 cur 和 cur.next 是否为空。

  3. 处理重复元素:

    java

    复制代码
    if (cur.val == cur.next.val) {
        // 跳过重复节点
        cur.next = cur.next.next;
    } else {
        // 移动到下一个不重复的节点
        cur = cur.next;
    }
    • 如果当前节点和下一个节点的值相同,修改 cur.next 指针,跳过下一个节点

    • 如果值不同,正常移动到下一个节点

复杂度分析

时间复杂度: O(n)

  • 只需要遍历链表一次,每个节点最多被访问一次

空间复杂度:

  • 直接遍历法:O(1),只使用了常数级别的额外空间

常见错误

错误示例

java 复制代码
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode cur = head;
        while (cur != null) {
            ListNode p = cur.next;
            if (p.val != cur.val) {  // 可能空指针异常
                cur = p;
            }
            cur.next = p.next;  // 逻辑混乱
        }
        return head;
    }
}

错误分析:

  1. 空指针异常 :当 cur.next 为 null 时,p 为 null,访问 p.val 会导致空指针异常

  2. 逻辑错误 :在 if 语句内部移动 cur,然后又在外部修改 cur.next,逻辑不清晰

  3. 可能跳过不重复节点 :当 p.val != cur.val 时,代码将 cur 移动到 p,然后立即修改 cur.next = p.next,这会跳过 p 节点

扩展思考

如果链表未排序怎么办?

如果链表未排序,我们需要使用额外的数据结构(如HashSet)来记录已经出现的元素。时间复杂度仍然是O(n),但空间复杂度变为O(n)。

如何删除所有重复元素(LeetCode 82)?

LeetCode 82要求删除所有重复出现的元素,只保留没有重复出现的元素。这需要更复杂的指针操作,因为需要完全删除重复元素,而不仅仅是去重。

总结

删除排序链表中的重复元素是一个基础的链表操作问题,关键在于理解指针的操作。直接遍历法是最常用的解法,具有O(1)的空间复杂度。递归解法虽然简洁,但有O(n)的空间复杂度。在实际面试中,建议使用直接遍历法,并注意处理边界条件。

关键技巧:

  1. 使用双指针或单指针遍历链表

  2. 比较相邻节点的值

  3. 通过修改指针来跳过重复节点

  4. 注意处理空指针异常

掌握了这个问题,可以帮助你更好地理解链表的基本操作,为更复杂的链表问题打下基础。

相关推荐
海鸥-w3 小时前
什么是AOP
java
Cc.Y4 小时前
Java零基础入门:可变字符串与包装类:StringBuilder、StringBuffer 与 通讯录管理系统实战
java·开发语言·python
paopaokaka_luck4 小时前
基于springboot+vue的智慧家政管理系统(AI问答、 WebSocket实时沟通、ECharts运营分析、订单规则与服务追踪)
java·javascript·spring boot·数据分析·echarts·mybatis
sm_926787054 小时前
RFID 标签打印的技术实现要点与二次开发实践
java·大数据·前端·c++·编辑器
阿森_dev4 小时前
2027 届毕设选题怎么选:30 个 Java 方向选题的实现难度排序
java·开发语言·spring boot·毕业设计·课程设计
Sarvartha4 小时前
Lambda 表达式和函数式接口
java·开发语言
计算机毕业设计杰瑞4 小时前
【计算机毕设精品】基于SpringBoot的一体化教务与学业质量管理系统的设计与实现,附源码_增删改查_java项目_可完美运行部署_网站项目
java·spring boot·课程设计
colinu5 小时前
C++ 异步任务与线程生命周期:`std::async`、`future`、`thread`、`jthread` 与协作式退出
java·jvm·c++
蜗牛互联网5 小时前
Python + JSON Schema 实现工单结构化输出与本地复核
java·人工智能·后端
逐光者9335 小时前
STM32——SPI 屏 · Flash · 高速
java·前端·stm32