DAY-1 | Java数据结构之链表:删除无头单链表中等于给定值 val 的所有节点

简介: 力扣203题解:使用时间复杂度为O(n)的思路删除链表中所有值为key的元素。引入辅助指针pre,记录cur的前一个节点,遍历链表时,若cur.val!=key,pre和cur同时前进;若cur.val==key,则pre.next=cur.next,cur继续前进,确保pre不急于跟随以处理连续相同值的情况。遍历结束后,处理头节点可能需要删除的特殊情况。

一、题干


🔗力扣203. 移除链表元素




二、题解


1、思路


题干的意思是,要删除链表中所有指定的元素。最暴力的方法是,依次遍历链表中的各个节点,并挨个调用“删除第一次出现关键字为key的节点”的方法remove():



     //删除第一次出现关键字为key的节点
     public void remove(int key) {
         //排除特殊情况
         if(head == null) {
             return;
         }
         if(head.val == key) {
             head = head.next;
             return;
         }
 
         //遍历链表
         Node cur = head;
         while(cur.next != null) {
             //寻找待删除元素的第一个
             if(cur.next.val == key) {
                 break;
             }
             cur = cur.next;
         }
         //特殊情况判断
         if(cur.next == null) {
             return;
         }
         //删除元素
         cur.next = cur.next.next;
     }


这样的思路实现后,代码的时间复杂度是O(n²)。


如果我们只遍历一遍链表,用时间复杂度是O(n)的思路,该如何实现呢?


我们可以引入一个新的“跟屁虫”引用变量pre,记录用于遍历的引用变量cur的前一个结点地址。通过pre的协助,完成删除单链表中所有指定元素的效果。


如图,初始状态下,pre的起点为head,而cur的起点为head.next。pre此时正是cur的前一个结点。



在遍历链表的过程中,通过比较 cur.val 与 key来寻找待删除的元素key。如果cur.val != key,那么cur与pre均需接着向前走,跟屁虫pre仍然记录cur的前一个结点位置。



cur走到上图这个位置时,cur.val 就与key相等了。也就是说,cur找到了一个要删除的结点。此时,我们将cur所指向的结点删除。这时pre的巨大作用便体现出来了:



将pre.next直接指向cur.next,此时的cur所指向的结点便成功地排除在链表之外了。完成这一步后,再让cur向后走。


注意,当cur.val == key时,我们完成remove操作后只需要让cur向后走就行,pre先不急着跟上。这是因为可能存在有两个待删元素连续的情况。如下图所示:



第一个val为6的结点已经被删除,而它后面还是一个val为6的结点,也应该被删除。此时pre不跟上,pre.next = cur.next依旧可以完成删除操作。



而若pre依然跟上:



cur.next无法接回原链表,删除失败,会出现混乱。


当所有结点都遍历完毕也即cur == null后,循环结束。


因此,一般情况下的代码即是:


         //起始
         Node cur = head.next;
         Node pre = head;
         while(cur != null) {
             //是要删除的元素
             if(cur.val == key) {
                 //删除操作
                 pre.next = cur.next;
                 //只有cur向后走
                 cur = cur.next;
             } else {    //不是要删除的元素
                 //pre和cur都向后走,且pre指向cur的前一个
                 pre = cur;
                 cur = cur.next;
             }
         }


特殊情况有两个:


1、链表为空,head为null。此时若直接执行cur = head.next,会抛出空指针异常。因此需要单独考虑。


2、head结点的val恰好是key。此时,有两种处理方式:


①方式1,在起始开始前,加入如下代码:


//方式1
while(head.val == key){
    head = head.next;
}
 
//起始
Node cur = head.next;
Node pre = head;
while(cur != null) {
    ...
}


②方式2,在遍历完链表后面的所有元素之后,再加入如下代码:


        //起始
        Node prev = head;
        Node cur = head.next;
        while (cur != null) {
            ...
        }
    
        //方法2
        if(head.val == key) {
            head = head.next;
        }


2、完整代码


     //删除所有值为key的节点
     public void removeAllKey(int key) {
         //特殊情况处理:链表为null
         if(head == null){
             return;
         }
 
         //起始
         Node cur = head.next;
         Node pre = head;
         while(cur != null) {
             if(cur.val == key) {
                 pre.next = cur.next;
                 cur = cur.next;
             } else {
                 pre = cur;
                 cur = cur.next;
             }
         }
         //如果头结点中的值恰好是要删除的值
         if(head.val == key) {
             head = head.next;
         }
     }



相关文章
|
4月前
|
机器学习/深度学习 算法
24. 两两交换链表中的节点, 19.删除链表的倒数第N个节点 ,面试题 02.07. 链表相交
1. **两两交换链表中的节点**:通过引入虚拟头结点,使所有节点都能采用统一的交换逻辑,避免对头结点单独处理。 2. **删除链表的倒数第N个节点**:利用双指针技巧,让快慢指针保持N个节点的距离,当快指针到达末尾时,慢指针正好指向待删除节点的前一个节点。 3. **链表相交**:先计算两链表长度并调整起点,确保从相同距离末尾的位置开始遍历,从而高效找到相交节点或确定无交点。 以上方法均在时间复杂度和空间复杂度上进行了优化,适合用于理解和掌握链表的基本操作及常见算法设计思路。
|
11月前
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
172 4
|
8月前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
200 30
|
8月前
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
313 25
|
9月前
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】单链表的基本运算(头歌实践教学平台习题)【合集】
本内容介绍了单链表的基本运算任务,涵盖线性表的基本概念、初始化、销毁、判定是否为空表、求长度、输出、求元素值、按元素值查找、插入和删除数据元素等操作。通过C++代码示例详细解释了顺序表和链表的实现方法,并提供了测试说明、通 - **任务描述**:实现单链表的基本运算。 - **相关知识**:包括线性表的概念、初始化、销毁、判断空表、求长度、输出、求元素值、查找、插入和删除等操作。 - **测试说明**:平台会对你编写的代码进行测试,提供测试输入和预期输出。 - **通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了测试通过后的预期输出结果。 开始你的任务吧,祝你成功!
337 5
|
10月前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
11月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
282 5
|
11月前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
807 4
|
11月前
|
算法
数据结构之购物车系统(链表和栈)
本文介绍了基于链表和栈的购物车系统的设计与实现。该系统通过命令行界面提供商品管理、购物车查看、结算等功能,支持用户便捷地管理购物清单。核心代码定义了商品、购物车商品节点和购物车的数据结构,并实现了添加、删除商品、查看购物车内容及结算等操作。算法分析显示,系统在处理小规模购物车时表现良好,但在大规模购物车操作下可能存在性能瓶颈。
237 0
|
11月前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
314 0