剑指offer-56、删除链表中重复的节点

题⽬描述

在⼀个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表 1->2->3->3->4->4->5 处理后为 1->2->5

示例1 输⼊:{1,2,3,3,4,4,5} 返回值:{1,2,5}

思路及解答

hash统计

第一次遍历统计频率,第二次遍历删除重复节点

java 复制代码
import java.util.HashMap;

public class Solution {
    public ListNode deleteDuplication(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        
        // 第一次遍历:统计每个节点值出现的次数
        HashMap<Integer, Integer> countMap = new HashMap<>();
        ListNode current = head;
        while (current != null) {
            countMap.put(current.val, countMap.getOrDefault(current.val, 0) + 1);
            current = current.next;
        }
        
        // 第二次遍历:删除重复节点
        ListNode dummy = new ListNode(-1); // 哑节点简化边界处理
        dummy.next = head;
        ListNode prev = dummy;
        current = head;
        
        while (current != null) {
            if (countMap.get(current.val) > 1) {
                // 当前节点重复,跳过
                prev.next = current.next;
            } else {
                // 当前节点不重复,移动prev指针
                prev = prev.next;
            }
            current = current.next;
        }
        
        return dummy.next;
    }
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

直接遍历(推荐)

注意,题目已经提到是排序的节点,那么就可以直接原地删除

对⽐前后两个元素,如果相同的情况下,接着遍历后⾯的元素,直到元素不相等的时候,将前⾯的指针指向最后⼀个相同的元素的后⾯,相当于跳过了相同的元素。

java 复制代码
public class Solution {
    public ListNode deleteDuplication(ListNode pHead)
    {
        //遍历链表,直接删除
        if(pHead == null || pHead.next == null) return pHead;
        ListNode head = new ListNode(0);
        head.next = pHead;
        ListNode cur = head.next;
        ListNode pre = head;
        while(cur != null){
            //将重复的结点都遍历过,然后将后面节点复制给pre结点后面
            if(cur.next != null && cur.val == cur.next.val){
                while(cur.next != null &&  cur.val == cur.next.val){
                    cur = cur.next;
                }
                pre.next = cur.next;
                cur = cur.next;
            }else{
                pre = pre.next;
                cur = cur.next;
            }
        }
        return head.next;
    }
}
  • 空间复杂度为 O(1) ,没有借助额外的空间
  • 时间复杂度为 O(n) ,只遍历了⼀次链表

递归

将大问题分解为当前节点+剩余链表的子问题

java 复制代码
/**
 * 递归法:分治思想解决子问题
 * 思路:将大问题分解为当前节点+剩余链表的子问题
 * 
 */
public class Solution {
    public ListNode deleteDuplication(ListNode head) {
        // 递归终止条件:空链表或单节点链表
        if (head == null || head.next == null) {
            return head;
        }
        
        // 情况1:当前节点与下一节点重复
        if (head.val == head.next.val) {
            // 跳过所有重复节点,找到第一个不重复的节点
            ListNode node = head.next;
            while (node != null && head.val == node.val) {
                node = node.next;
            }
            // 递归处理剩余部分
            return deleteDuplication(node);
        } 
        // 情况2:当前节点不重复
        else {
            head.next = deleteDuplication(head.next);
            return head;
        }
    }
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(n) ,递归栈空间

三指针法

使用pre、cur、next三个指针精确控制删除范围

java 复制代码
public class Solution {
    public ListNode deleteDuplication(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode pre = dummy;    // 前驱指针
        ListNode cur = head;     // 当前指针
        ListNode next = null;     // 后继指针
        
        while (cur != null && cur.next != null) {
            next = cur.next;
            
            // 发现重复节点
            if (cur.val == next.val) {
                // 移动next直到找到不重复的节点
                while (next != null && cur.val == next.val) {
                    next = next.next;
                }
                // 跳过所有重复节点
                pre.next = next;
                cur = next;
            } 
            // 没有重复,正常移动指针
            else {
                pre = cur;
                cur = cur.next;
            }
        }
        
        return dummy.next;
    }
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)
相关推荐
怨言.3 分钟前
Java内部类详解:从基础概念到实战应用(附案例)
java·开发语言
XiYang-DING5 分钟前
【Java】 Java 集合框架
java·开发语言
心勤则明11 分钟前
Spring AI Alibaba Skills 的渐进式披露与热更新实战
java·后端·spring
netyeaxi19 分钟前
Spring:如何查看Spring应用对外提供了哪些API接口?
java·spring
一只大袋鼠20 分钟前
MySQL 事务从入门到精通(上):概念、操作、特性、隔离级别全解析
java·mysql·事务
若鱼191929 分钟前
JPA/Hibernate中一对一关联时不持有外键方的属性延迟加载为什么不生效?
java·spring
砍材农夫38 分钟前
spring-ai 第八模型介绍-图像模型
java·人工智能·spring
橘子hhh1 小时前
Netty基础服务器实现
java·nio
墨雪遗痕1 小时前
工程架构认知(二):从 CDN 到 Keep-Alive,理解流量如何被“消化”在系统之外
java·spring·架构
用户6688599847661 小时前
Sprint Boot登录案例
java