力扣 单链表元素删除解析及高频面试题

目录

删除元素的万能方法

构造虚拟头结点来应对删除链表头结点的情况

一、203.移除链表元素

题目

题解

二、19.删除链表中倒数第K个节点

题目

题解

三、

83.删除某个升序链表中的重复元素,使重复的元素都只出现一次 

题目

题解

82.删除某个升序链表中的重复元素且不保留,即只含原始链表中没有重复出现过的元素

题目

题解


删除元素的万能方法

删除元素的万能方法,就是找到目标节点,保存目标节点的前驱节点的引用,让前驱结点的next,指向目标节点的后继节点。这样,没有目标节点的引用,它将被gc回收。

  • 这里cur是前驱节点的引用,cur.next指向目标节点。删除元素步骤如下:首先我们在题目中通过各种判断,确定cur.next指向的,就是我们需要删除的目标节点 。然后,把cur.next.next赋值给cur.next。这也就是说,让cur.next也指向后继结点。
  • 此时前驱结点的next已经指向了后继结点。那原来的目标节点呢?已经不连接在链表中了,并因其没有引用变量指向,将会被gc回收。

但这个时候有些细心的小伙伴可能会有疑问:你这删除是讲清楚了,但是我发现这样的删除需要有前驱节点和后继节点,那如果是删除链表头结点(没有前驱结点)或是删除链表尾结点(没有后继结点),那怎么办呢?哈哈,先告诉结论:链表尾结点的状况,上述做法可以包含进来。而链表头结点,要么分情况讨论,要么使用我们接下来使用的方法:建立虚拟链表头结点。

构造虚拟头结点来应对删除链表头结点的情况

在上文中我们得出尾结点是包含在cur.next = cur.next.next中的。但是对于头结点就不好使了。为什么呢?因为如果后继结点是null,那还可以把null赋给一个引用,但是如果前驱结点cur为null,那么cur.next就会直接抛出一个异常了!那我们怎么办呢?第一种方法是if-else判断,如果删除元素是头结点就直接返回head.next。但是这样写的话代码比较乱,这里介绍一种更好的办法:构造虚拟头结点。这是啥意思呢?所谓虚拟头结点,就是在原链表的头结点前方接上一个节点。这样,原链表的所有结点在新链表中都不是头结点了,自然就可以沿用上面的赋值式。

如图,先创建虚拟头结点temp(值设为-1),然后让它的next指向原链表头结点,然后让cur初始化为temp(cur = temp),这样cur就永不为null,删除节点永远有前驱节点,cur.next = cur.next.next就可以继续使用。

这样我们就成功用cur.next = cur.next.next完成了头结点的删除。

需要注意的是,此方法到最后,返回删除元素后的新链表的头结点时,应该返回的是temp.next,而不是temp。

一、203.移除链表元素

题目

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

题解

通过在头节点前增加虚拟头节点(哨兵),头结点则变为普通结点,则不需要判断头节点是否为待删除的节点,但是在返回的时候需要返回的是虚拟头节点的下一节点而不是虚拟头节点。

public class ListNode {public int val;public ListNode next;
}
public static ListNode deleteListNode(ListNode head,int val){//删除所有val结点,删除节点的方式是找到前驱节点cur,让cur.next = cur.next.nextListNode temp = new ListNode(-1);temp.next = head;ListNode cur = temp;//构造了带虚拟头结点的链表并初始化cur//用cur.next.val判断是因为cur.next.val == val判断cur.next是否为目标节点,//这样cur就是前驱结点的引用了。我们在删除节点时必须考虑到保留前驱结点引用。while (cur.next != null){if (cur.next.val == val){cur.next = cur.next.next;//删除目标节点。cur不移动,继续与下一个节点比较}else {cur = cur.next;//如果当前cur.next不符合删除要求,cur 向链表后方移动一次再次比较}}return temp.next;
}

二、19.删除链表中倒数第K个节点

题目

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

提示:
链表中结点的数目为 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz

题解

这里我们使用双指针法,先让快慢指针步差为K+1,然后快慢指针同步移动,这样当快指针指向链表末尾的null时(你也可以理解成链表最后一个元素是倒数第1节点,最后一个元素的null指针域是倒数第0节点),即快指针指向倒数第0个节点时,因为步差K+1,所以这时慢指针指向倒数第K+1个节点,也就是前驱节点。不知道你发现没有,2)题这类问题的核心,就是在于寻找删除节点的前驱结点。

然后按部就班的删除即可。

public static ListNode deleteLastKByTwoPointers(ListNode head,int k){ListNode temp = new ListNode(-1);temp.next = head;ListNode slow = temp;ListNode fast = temp;//初始化,虚拟头结点构造,快慢指针指向虚拟头结点//fast先走k+1步for (int i = 0; i < k+1; i++) {fast = fast.next;}while (fast != null){fast = fast.next;slow = slow.next;}//同步前进直到fast为nullslow.next = slow.next.next;//删除待删除的节点return temp.next;
}

三、

这类问题的关键在于你的cur引用是指向第一个重复元素,还是指向第一个重复元素的前驱结点。

(1)如果cur指向第一个重复元素,那么把cur.val和cur.next.val比较,如果相等就删除cur.next,这样就删除了所有和cur.val相同值的节点,但还留下了一个cur节点。这样会留下一个重复节点,即LeetCode83。

  • cur.val==cur.next.val ?删除cur.next:cur向后移动
  • 最后重复元素留下一个节点0

(2)如果cur指向第一个重复元素的前驱节点,那么把cur.next.val和cur.next.next.val比较,如果相等就存下重复元素的值(6),然后cur.next.val逐个与存储值比较,是就删除cur.next,这样就删除了所有重复结点。这样重复值节点一个也不会留下,即LeetCode82。

  • 如果cur.next.val==repeatData,循环删除直到没有repeatData节点为止

83.删除某个升序链表中的重复元素,使重复的元素都只出现一次 

题目

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

提示:
链表中节点数目在范围 [0, 300] 内
-100 <= Node.val <= 100
题目数据保证链表已经按升序 排列

题解

由于给定的链表是排好序的,因此重复的元素在链表中出现的位置是连续的,这个很关键。因此我们只需要对链表进行一次遍历,就可以删除重复的元素。

public static ListNode deleteRepeatAndSaveOne(ListNode head){if (head == null){return null;}
//这题不用构建虚拟头结点ListNode cur = head;while (cur.next != null){//逐个逐个比较直到后继结点为nullif (cur.val == cur.next.val){//如果值相同,则删除cur.next, cur不移动,和下一个cur.next继续比较cur.next = cur.next.next;}else {cur = cur.next;//如果值不同,则移动cur至后继结点,开始下一轮比较}}return head;
}

82.删除某个升序链表中的重复元素且不保留,即只含原始链表中没有重复出现过的元素

题目

给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。

提示:
链表中节点数目在范围 [0, 300] 内
-100 <= Node.val <= 100
题目数据保证链表已经按升序 排列

题解

这道题和上一道题类似83. 删除排序链表中的重复元素,但是这里要求的是删除所有重复的链表节点,而上一题可以保留一个。
分析一下这道题,删除所有重复的节点,加入有两个节点被删除,例如[1,2,2,3],那么在进行对比的时候需要保留一个节点在1的位置,寻找重复的两个指针一个左边的在第一个2,右边的第一次在第二个2,第二次在3。这个时候我们需要把1->3连接起来。

流程如下:

  1. 一边遍历、一边统计相邻节点的值是否相等,如果值相等就继续后移找到值不等的位置,然后删除值相等的这个区间。
    1. 设置一个虚拟节点,将虚拟头节点和原链表头节点连接起来
    2. 从虚拟头节点位置开始访问
    3. 只要当前访问节点的下一个节点与下下个节点都存在,就继续访问下去
    4. 在访问过程中,如果下一个节点与下下个节点相同,那么说明与这个节点值相同的所有节点都应该被删除掉。
  2. 删除的方法是先记录这个值,利用 while 循环,不断的查找出那些相同的节点值来,每次找到了一个相同的值,那么当前访问的节点 cur 就越过这个节点。
  3. 在访问过程中,下一个节点与下下个节点不相同,说明 cur 可以加入到最终的结果链表中,那么继续访问后面的节点。

 代码如下:

class Solution {public ListNode deleteDuplicates(ListNode head) {// 一开始设置一个虚拟节点,它的值为 -1,它的值可以设置为任何的数,因为我们根本不需要使用它的值ListNode dummy = new ListNode(-1);// 将虚拟头节点和原链表头节点连接起来// 添加虚拟头节点之后,原链表的每个节点的地位都是一样的dummy.next = head;// 从虚拟头节点位置开始访问ListNode cur = dummy;// 只要当前访问节点的下一个节点与下下个节点都存在,就继续访问下去while (cur.next != null && cur.next.next != null) {// 在访问过程中,会出现两种情况// 1、下一个节点与下下个节点相同,那么说明与这个节点值相同的所有节点都应该被删除掉if (cur.next.val == cur.next.next.val) {// 删除的方法是先记录这个值int value = cur.next.val;// 利用 while 循环,不断的查找出那些相同的节点值来while (cur.next != null && cur.next.val == value) {// 每次找到了一个相同的值,那么当前访问的节点 cur 就越过这个节点cur.next = cur.next.next;}// 2、下一个节点与下下个节点不相同,说明 cur 可以加入到最终的结果链表中} else {// 那么继续访问后面的节点cur = cur.next;}}// 最终返回虚拟头节点的下一个节点就行了return dummy.next;}
}

我们发现,其实单链表删除都是一样的套路。找好前驱节点,明晰删除条件,遍历小心谨慎,脑中不断构建图,其实单链表删除真的不难。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/365429.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mongodb在windows环境安装部署

一、mongodb 1.释义 MongoDB 是一种开源的文档型 NoSQL 数据库管理系统&#xff0c;使用 C 编写&#xff0c;旨在实现高性能、高可靠性和易扩展性。MongoDB 采用了面向文档的数据模型&#xff0c;数据以 JSON 风格的 BSON&#xff08;Binary JSON&#xff09;文档存储&#x…

第一周:李宏毅机器学习笔记

第一周学习周报 摘要一、机器学习基础理论1. 什么是机器学习&#xff1f;2. 机器学习“寻找”的函数有哪些类型&#xff1f;3. 机器学习中机器如何“寻找”函数&#xff1f;三步走3.1 第一步&#xff1a;设定函数的未知量&#xff08;Function with Unknown Parameters&#xf…

SpringMvc 执行原理

当用户请求 会发送到前端控制器&#xff0c;DisptcherServlet根据请求参数生成代理请求&#xff0c;找到对应的实际控制器&#xff0c;控制器处理请求&#xff0c;创建数据模型&#xff0c;访问数据库&#xff0c;将模型响应给中心控制器&#xff0c;控制器使用模型与视图渲染视…

09_计算机网络模型

目录 OSI/RM七层模型 OSI/RM七层模型 各层介绍及硬件设备 传输介质 TCP/IP协议簇 网络层协议 传输层协议 应用层协议 完整URL的组成 IP地址表示与计算 分类地址格式 子网划分和超网聚合 无分类编址 特殊含义的IP地址 IPv6协议 过渡技术 OSI/RM七层模型 OSI/RM七…

⭐Ollama的本地安装⚡

先来逛一下咱们的主角Ollama的官网地址&#xff1a; Ollama 大概长这个样子&#x1f914; 因为本地系统的原因&#xff0c;文章只提供Widows的安装方式&#xff0c;使用Linux和Mac的大佬&#xff0c;可以自行摸索&#x1f9d0; 下载完成后就是安装了&#x1f355;&#xff0c…

黑龙江等保测评科普

黑龙江的等保测评&#xff0c;即信息安全等级保护测评&#xff0c;是中国网络安全法框架下的一项重要制度&#xff0c;旨在提升信息系统安全水平&#xff0c;保护关键信息基础设施免受威胁。下面是对黑龙江等保测评流程和要求的科普&#xff1a; 1. 等保测评概念 定义&#xff…

【正点原子K210连载】 第十二章 跑马灯实验 摘自【正点原子】DNK210使用指南-CanMV版指南

1&#xff09;实验平台&#xff1a;正点原子ATK-DNK210开发板 2&#xff09;平台购买地址https://detail.tmall.com/item.htm?id731866264428 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/docs/boards/xiaoxitongban 第十二章 跑马灯实验…

线程实现模型

用户级线程模型 此模型下的线程是由用户级别的线程库全权管理的 线程库并不是内核的一部分&#xff0c;而只是存储在进程的用户空间之中&#xff0c;这些线程的存在在对于内核来说是无法感知的 应用程序在对线程进行创建、终止、切换或同步等操作的时候&#xff0c; 并不需要…

解题思路:LeetCode 第 209 题 “Minimum Size Subarray Sum“

解题思路&#xff1a;LeetCode 第 209 题 “Minimum Size Subarray Sum” 在这篇博文中&#xff0c;我们将探讨如何使用 Swift 解决 LeetCode 第 209 题 “Minimum Size Subarray Sum”。我们会讨论两种方法&#xff1a;暴力法和滑动窗口法&#xff0c;并对这两种方法的时间复…

阿里云centos 7.9 使用宝塔面板部署.netcore 6.0

前言&#xff1a; 在做工作之前之前&#xff0c;如果你的服务器有数据盘&#xff0c;而且又没挂载&#xff0c;但是你想使用数据盘做为工作目录&#xff0c;建议跳转到下面这个链接先挂载数据盘&#xff0c;并到数据盘创建好目录&#xff0c;修改站点工作目录到数据盘的目录&am…

骑行十里箐:风景,挑战与心灵,在幽谷中的协奏曲

2024年6月29日&#xff0c;星期六&#xff0c;一个看似平凡的日子&#xff0c;却因一次不同寻常的骑行而变得难以忘怀。作为校长骑行群的一员&#xff0c;我有幸参加了这次骑行十里箐的活动。从滇池后海的宁静开始&#xff0c;到宝珠山顶的壮观落幕&#xff0c;这一天的旅程充满…

JFreeChart 生成Word图表

文章目录 1 思路1.1 概述1.2 支持的图表类型1.3 特性 2 准备模板3 导入依赖4 图表生成工具类 ChartWithChineseExample步骤 1: 准备字体文件步骤 2: 注册字体到FontFactory步骤 3: 设置图表具体位置的字体柱状图&#xff1a;饼图&#xff1a;折线图&#xff1a;完整代码&#x…

Quectel EM05-CE 模块测试

作者简介&#xff1a; 一个平凡而乐于分享的小比特&#xff0c;中南民族大学通信工程专业研究生在读&#xff0c;研究方向无线联邦学习 擅长领域&#xff1a;驱动开发&#xff0c;嵌入式软件开发&#xff0c;BSP开发 作者主页&#xff1a;一个平凡而乐于分享的小比特的个人主页…

AI绘画:探索人工智能与艺术的奇妙结合

前言 人工智能技术的不断发展&#xff0c;使得AI绘画逐渐成为艺术领域的新宠。AI绘画是指利用人工智能算法进行绘画创作的一种艺术形式&#xff0c;它可以模拟人类艺术家的创作过程&#xff0c;创造出各种独特的艺术作品。 突破传统艺术的极限&#xff1a;AI绘画的无限可能性 …

每日一题---OJ题:分隔链表

片头 嗨&#xff01;小伙伴们&#xff0c;大家好&#xff01;今天我们一起来看看这道题----分隔链表 emmmm&#xff0c;这道题&#xff0c;看描述应该不算太难&#xff0c;我们一起来画一画图呗&#xff01; 题目读懂了&#xff0c;那么如何破解这道题呢&#xff1f; 思路&…

探索PcapPlusPlus开源库:网络数据包处理与性能优化

文章目录 0. 本文概要1. PcapPlusPlus介绍1.1 概述1.2主要特性和功能1.3 PcapPlusPlus 主要模块关系和依赖1.4 网络协议层处理过程 2. 实例2.1 基于 PcapPlusPlus 的应用程序设计和封装流程&#xff1a;2.2 多线程示例代码2.3 代码说明&#xff1a; 3. 程序性能进一步优化3.1 避…

linux虚拟机部署的MySQL如何使用外网访问?教你轻松使用cpolar在centos搭建内网穿透

文章目录 写在前面实现Linux的内网穿透1、官网账号注册2、在Linux部署我们自己的项目3、一键自动下载安装cpolar4、设置自己的token5、启动cpolar服务6、MySQL穿透测试 卸载方法 写在前面 相信很多小伙伴在本地搭建了一个MySQL数据库&#xff0c;想让其他同事或者合作者一起使…

Shell编程实战

脚本编程步骤 脚本编程一般分为以下几个步骤: 需求分析:根据系统管理的需求&#xff0c;分析脚本要实现的功能、功能实现的层次、实现的命令与语句等; 命令测试:将要用到的命令逐个进行测试&#xff0c;以决定使用的选项、要设置的变量等: 脚本编程:将测试好的命令写入到脚本文…

python实现网页自动化(自动登录需要验证的网页)

引言: python作为实现网页自动化的一个重要工具,其强大的各种封装的库使得程序运行更加简洁,只需要下载相应的库,然后调用库中的函数就可以简便的实现我们想要的网页相关操作。 正文: 我的前几篇文章写了关于初学爬虫中比较容易上手的功能,例如爬取静态网页的数据、动…

Java并发编程基础知识点

目录 Java并发编程基础知识点1、线程&#xff0c;进程概念及二者的关系进程相关概念线程相关概念进程与线程的关系补充小知识点&#xff1a; 2、线程的状态Java线程的状态&#xff1a;Java线程不同状态之间的切换图示 3、Java程序中如何创建线程&#xff1f;①、继承Thread类②…