力扣 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. 注意处理空指针异常

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

相关推荐
重生之我是Java开发战士几秒前
【Redis】Java & JavaSpring 客户端
java·redis·bootstrap
郭小李23 分钟前
mybatis的 Mapper.xml语法
xml·java·mybatis
小蒜学长26 分钟前
基于SpringBoot 框架的自媒体新闻系统的设计与实现(代码+数据库+LW)
java·spring boot·后端·mysql数据库·自媒体新闻管理系统
96245627 分钟前
补打一条卡,为什么要从前一天重新算:人工补录与实时重算业务复盘
java·mysql·kafka
Java后端的Ai之路36 分钟前
01、Java并发编程:学习攻略如何才能学好并发编程?
java·开发语言·学习
Pniubi1 小时前
力扣42接雨水(动态规划法)
算法·leetcode·职场和发展
ScilogyHunter1 小时前
Hercules系列安全MCU功能安全原理详解
java·单片机·安全·hercules
博风1 小时前
eclipse中配置maven,以及在本地使用maven。
java·eclipse·maven
云-缩影1 小时前
实际项目中怎么选排序算法:从面试题到工程落地
java·算法·排序算法
追星星の人1 小时前
java web项目(eclipse软件)全流程备份
java·servlet