力扣 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 指针遍历链表,需要同时检查 curcur.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.nextnull 时,pnull,访问 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. 注意处理空指针异常

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

相关推荐
爱笑的源码基地13 小时前
高并发 Redis 缓存门诊HIS系统源码,含财务统计药房进销存
java·程序·门诊系统·诊所系统·云诊所源码
莫逸风15 小时前
【AgentScope 2.0】 0. 学习指南
java·llm·agent·agentscope
z1234567898616 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
yaoxin52112316 小时前
470. Java 反射 - Member 接口与 AccessFlag
java·开发语言·python
做个文艺程序员17 小时前
Linux第24篇:Java应用监控体系搭建:Prometheus+Grafana可视化运维
java·grafana·prometheus
小钻风336617 小时前
Spring Boot 文件上传详解:深入理解 MultipartFile 的使用与原理
java·开发语言
其美杰布-富贵-李17 小时前
Spring Boot 工程开发全流程说明
java·spring boot·后端
前端双越老师19 小时前
如何以前端视角(非0基础)学 Java ?
java·node.js·全栈
dear_bi_MyOnly19 小时前
【SpringBoot配置文件】
java·spring boot·后端·学习·spring·java-ee·学习方法
做个文艺程序员19 小时前
Linux第22篇:用Docker容器化你的Java SaaS应用:一次构建,随处运行
java·docker·容器