链表高频题目和必备技巧
链表高频题目和必备技巧
1. 链表类题目注意点
1,如果笔试中空间要求不严格,直接使用容器来解决链表问题
2,如果笔试中空间要求严格、或者在面试中面试官强调空间的优化,需要使用额外空间复杂度**O(1)**的方法
3,最常用的技巧-快慢指针
4,链表类题目往往都是很简单的算法问题,核心考察点也并不是算法设计,是coding能力
5,这一类问题除了多写多练没有别的应对方法
注意: 链表类问题既然练的就是coding,那么不要采取空间上讨巧的方式来练习
注意: 链表相关的比较难的问题是约瑟夫环问题,会在之后补充
2. 相关题目
注意:
这些题目往往难度标为“简单”,是因为用容器解决真的很简单
但是不用容器、实现额外空间复杂度O(1)的方法并不轻松,包括很多提交的答案也都没有符合要求
-
题目1 : 返回两个无环链表相交的第一个节点
-
测试链接 : https://leetcode.cn/problems/intersection-of-two-linked-lists/
-
思路
- 先判断是否不相交
- 若有一个为空则不相交
- 长链表提前走差值个, 随后两链表一同走, 走到尾巴, 若不相同, 则不相交
- 两链表从头走, 直至寻找到第一个公共节点
- 先判断是否不相交
-
代码
-
public static ListNode getIntersectionNode(ListNode h1, ListNode h2) {// 1. 先判断是否不相交// 边界: 只要有一个为空就不相交if (h1 == null || h2 == null) {return null;}ListNode a = h1, b = h2;// 计算两个链表长度之差int diff = 0;while (a.next != null) {a = a.next;diff++;}// a来到最后一个结点while (b.next != null) {b = b.next;diff--;}// b来到最后一个结点// 边界: 如果两个链表的尾结点不相等, 则一定不相交if (a != b) {return null;}// 2. 寻找第一个公共结点// 如果长度不等,把长链表给a,短链表给bif (diff >= 0) {a = h1;b = h2;} else {a = h2;b = h1;}// 取差值的绝对值diff = Math.abs(diff);// 长链表先走差值步while (diff-- != 0) {a = a.next;}// 长链表和短链表一起走while (a != b) {a = a.next;b = b.next;}// 当再次相交的时候停止return a;}
-
-
-
题目2 : 每k个节点一组翻转链表
-
测试链接:https://leetcode.cn/problems/reverse-nodes-in-k-group/
-
思路
-
由于要返回总的头结点, 单独讨论第一组
- 第一组够不够k个, 不够直接返回head
- 第一组反转(反转的过程中, lastTeamEnd.next指向下一组的头), 并记录反转后的头,用于返回
- 反转后记录lastTeamEnd为原来的head(start),
-
讨论接下来其他组
-
判断接下来的组够不够k个, 不够直接返回
-
够了,翻转,调整将上一组的尾连到这一组的头, lastTeamEnd来到这一组的尾
-
一致循环到没有下一组
-
-
-
代码
-
public static ListNode reverseKGroup(ListNode head, int k) {// 1. 先讨论第一组ListNode start = head;// 看第一组够不够k个ListNode end = teamEnd(start, k);if (end == null) {return head;}// 第一组 很特殊因为牵扯到换头的问题head = end;// 反转后头变成尾reverse(start, end);// 在反转的过程中会连上下一组// 翻转之后start变成了上一组的结尾节点ListNode lastTeamEnd = start;// 前一组的头变成了尾// 2. 接下来的其他组while (lastTeamEnd.next != null) {// 下一组还有没有start = lastTeamEnd.next;// 下一组的初头end = teamEnd(start, k);// 下一组的初尾if (end == null) {// 不够return head;// 直到下一组不够k个}reverse(start, end);// 够了,反转lastTeamEnd.next = end;// 上一组的尾巴要连到这一组的end(反转后变成头)lastTeamEnd = start;// 该组变为要调整的下一组的上一组}return head;// 直到没有下一组}// 当前组的开始节点是s,往下数k个找到当前组的结束节点返回public static ListNode teamEnd(ListNode s, int k) {while (--k != 0 && s != null) {// 当前计数s = s.next;// 走向下一个}return s;}// s -> a -> b -> c -> e -> 下一组的开始节点// 上面的链表通过如下的reverse方法调整成 : e -> c -> b -> a -> s -> 下一组的开始节点public static void reverse(ListNode s, ListNode e) {e = e.next;// 先保存下一组的第一个节点ListNode pre = null, cur = s, next = null;while (cur != e) {// 当前节点不是下一组的结点, 反转next = cur.next;cur.next = pre;pre = cur;cur = next;}s.next = e;// 将下一组的结点连在反转后的尾巴上}
-
-
-
题目3 : 复制带随机指针的链表
-
测试链接 : https://leetcode.cn/problems/copy-list-with-random-pointer/
-
思路
- 将原来的结点每一个都赋值一份放在原节点的后面, 将原节点和现节点串在一起
- 利用新老节点的关系, 设置每一个新节点的random指针
- 老链表分离 : 老链表重新连在一起,新链表重新连在一起
- 返回新链表的头节点
-
代码
-
public static Node copyRandomList(Node head) {// 特殊处理if (head == null) {return null;}// 克隆// 1 -> 2 -> 3 -> ...// 变成 : 1 -> 1' -> 2 -> 2' -> 3 -> 3' -> ...Node cur = head;Node next = null;while (cur != null) {next = cur.next;// 记录下一个cur.next = new Node(cur.val);// 克隆cur.next.next = next;// 原来的下一个接在克隆后的节点上cur = next;// 当前节点走到原来的下一个}// 利用上面新老节点的结构关系,设置每一个新节点的random指针cur = head;Node copy = null;while (cur != null) {next = cur.next.next;// 记录下一个值copy = cur.next;// 记录当前节点的克隆节点copy.random = cur.random != null ? cur.random.next : null;cur = next;}// 新老链表分离 : 老链表重新连在一起,新链表重新连在一起Node ans = head.next;// 记录新链表的头节点cur = head;while (cur != null) {next = cur.next.next;copy = cur.next;cur.next = next;// 原链表恢复原状copy.next = next != null ? next.next : null;cur = next;}// 返回新链表的头节点return ans;}
-
-
-
题目4 : 判断链表是否是回文结构。这个题的流程设计甚至是考研常用。快慢指针找中点。
-
测试链接 : https://leetcode.cn/problems/palindrome-linked-list/
-
思路
-
利用快慢指针找中点
-
找到后中点就是slow,从中点开始往后的节点逆序
-
开始头尾对照,设置临时变量进行处理,以保留原来的结点用于还原链表
-
把链表调整回原来的样子再返回判断结果
-
-
代码
-
public static boolean isPalindrome(ListNode head) {// 特例判断if (head == null || head.next == null) {// 没有/只有一个结点return true;}// 1. 快慢指针找中点ListNode slow = head, fast = head;while (fast.next != null && fast.next.next != null) {slow = slow.next;fast = fast.next.next;}// fast跳不了// 2. 现在中点就是slow,从中点开始往后的节点逆序// head -> ... -> slow -> ... -> ...ListNode pre = slow;ListNode cur = pre.next;ListNode next = null;pre.next = null;// ! 原来slow.next要先保留,赋值给cur, 随后中点的next要悬空(用于后续翻转时作为判断值)while (cur != null) {next = cur.next;// 以防cur->next改变后后续指针丢失cur.next = pre;pre = cur;cur = next;}// 3. 开始对照,设置临时变量进行处理,以保留原来的结点用于还原链表boolean ans = true;ListNode left = head;ListNode right = pre;// head -> ... -> slow <- ... <- pre// left往右、right往左,每一步比对值是否一样,如果某一步不一样答案就是falsewhile (left != null && right != null) {// 循环至中点的next == null,说明为回文结构if (left.val != right.val) {ans = false;break;// 先不返回, 把链表复原}left = left.next;right = right.next;}// 4. 把链表调整回原来的样子再返回判断结果// head -> ... -> slow <- ... <- pre// pre -> ... -> slowcur = pre.next;pre.next = null;next = null;while (cur != null) {// 中点的next为nullnext = cur.next;cur.next = pre;pre = cur;cur = next;}return ans;}
-
-
某考研题要求收尾挨个相接也用的同样的方法
-
-
题目5 : 返回链表的第一个入环节点。快慢指针找中点。
-
测试链接 : https://leetcode.cn/problems/linked-list-cycle-ii/
-
思路
- 特殊处理
- 设置双指针, 若f在跳的过程中先走到null, 说明无环, 返回
- 当fs相遇, f回到头结点, s不变
- f每次跳一步, s每次跳一步
- 最后相遇时, 一定是在入环的第一个节点
-
代码
-
public static ListNode detectCycle(ListNode head) {// 特殊处理: 为null, 只有一个结点, 只有两个结点且能到头if (head == null || head.next == null || head.next.next == null) {return null;}// 1. f跳两步 s跳一步ListNode slow = head.next;// 已经排除过特殊情况,直接往下跳ListNode fast = head.next.next;while (slow != fast) {// !!如果f先走到头, 说明没有环if (fast.next == null || fast.next.next == null) {return null;}slow = slow.next;fast = fast.next.next;}// 2. 相遇时, f回到头结点, s保持不变fast = head;// 3. f跳一步 s跳一步while (slow != fast) {slow = slow.next;fast = fast.next;}// 4. 最后相遇时一定是在入环的第一个节点return slow;}
-
-
-
题目6 : 在链表上排序。要求时间复杂度O(n * log n),额外空间复杂度O(1),还要求排序有稳定性。
-
测试链接 : https://leetcode.cn/problems/sort-list/
-
思路
- 计算链表的长度, 用于限制步长
- 开始按照步长进行排序
- 先处理第一组,因为排序后很可能要换头
- 继续处理其他组
-
代码
-
public static ListNode sortList(ListNode head) {// 计算链表的长度, 用于限制步长int n = 0;ListNode cur = head;while (cur != null) {n++;cur = cur.next;}// l1...r1 每组的左部分// l2...r2 每组的右部分// next 下一组的开头// lastTeamEnd 上一组的结尾ListNode l1, r1, l2, r2, next, lastTeamEnd;for (int step = 1; step < n; step <<= 1) {// 进来了,说明step<n,即n>=2// 第一组很特殊,因为要决定整个链表的头,所以单独处理// 找第一组的头尾, 第二组的头尾, 保存剩余链表的头部并将12组分离l1 = head;r1 = findEnd(l1, step);l2 = r1.next;r2 = findEnd(l2, step);next = r2.next;r1.next = null;r2.next = null;merge(l1, r1, l2, r2);head = start;// 临时保存lastTeamEnd = end;// 接下来的其他组while (next != null) {l1 = next;r1 = findEnd(l1, step);l2 = r1.next;if (l2 == null) {// 若l2不存在, 则无需进行合并, 直接停止lastTeamEnd.next = l1;// 尾接头,break不是return,因为要继续外层的循环break;}r2 = findEnd(l2, step);next = r2.next;r1.next = null;// !!!!都是断开r!!!!!!!!!!!!!r2.next = null;merge(l1, r1, l2, r2);lastTeamEnd.next = start;// 将上一组和调整好的这一组链接在一起lastTeamEnd = end;// 这一组的尾变成lastTeamEnd}}return head;}// 包括s在内,往下数k个节点返回// 如果不够,返回最后一个数到的非空节点public static ListNode findEnd(ListNode s, int k) {while (s.next != null && --k != 0) {s = s.next;}return s;}public static ListNode start;public static ListNode end;// l1...r1 -> null : 有序的左部分// l2...r2 -> null : 有序的右部分// 整体merge在一起,保证有序// 并且把全局变量start设置为整体的头,全局变量end设置为整体的尾public static void merge(ListNode l1, ListNode r1, ListNode l2, ListNode r2) {ListNode pre;// 找头if (l1.val <= l2.val) {start = l1;pre = l1;l1 = l1.next;} else {start = l2;pre = l2;l2 = l2.next;}// 合并while (l1 != null && l2 != null) {if (l1.val <= l2.val) {pre.next = l1;pre = l1;l1 = l1.next;} else {pre.next = l2;pre = l2;l2 = l2.next;}}// 找尾if (l1 != null) {pre.next = l1;end = r1;} else {pre.next = l2;end = r2;}}
-
-
相关文章:
链表高频题目和必备技巧
链表高频题目和必备技巧 1. 链表类题目注意点 1,如果笔试中空间要求不严格,直接使用容器来解决链表问题 2,如果笔试中空间要求严格、或者在面试中面试官强调空间的优化,需要使用额外空间复杂度**O(1)**的方法 3,最…...
Vue3详细介绍,正则采集器所用前端框架
Vue3 引入了一个全新的响应式系统,它是基于ES6的Proxy特性构建的。这个系统使得 Vue 能够更加高效地追踪数据的变化,并在数据发生变化时自动更新DOM。响应式系统的核心是"可观察",当数据变化时,视图会响应这些变化并重新…...
数据集--COCO2017(快速下载)
1、数据集介绍 数据集官网:https://cocodataset.org/#home COCO(Common Objects in Context)数据集是计算机视觉领域中最广泛使用的数据集之一,主要用于目标检测、分割和图像标注任务。COCO 数据集由 Microsoft 发布,…...
【管理咨询宝藏159】顶级咨询公司人力三支柱建设方案思路
阅读完整版报告内容,请搜索VV号“管理咨询宝藏”。 【管理咨询宝藏159】顶级咨询公司人力三支柱建设方案思路 【格式】PDF版本 【关键词】人力咨询、三支柱、人力体系 【核心观点】 - 集团总部制定全集团共享中心总体规划路径,组织并负责实施与推广。各…...
跨时钟域总结
跨时钟域总结 秋招学习跨时钟域 总结一下吧 异步电路 设计中有两个频率不同的时钟(也可能多个),而有数据在两组时钟之间传输 单bit跨时钟域 慢时钟域数据-> 快时钟域 方法 : 使用两个锁存器 (打两拍) 数据跨时钟域同步过程中,脉冲宽度会改变,不影响同步结…...
富婆和富公子都在看的负载均衡和Haproxy大全
一.负载均衡 1.1:什么是负载均衡 负载均衡: Load Balance ,简称 LB ,是一种服务或基于硬件设备等实现的高可用反向代理技术,负载均 衡将特定的业务(web 服务、网络流量等 ) 分担给指定的一个或多个后端特定的服务器或…...
VScode找python环境 (conda)
第一步 CtrlshiftP 第二步 框框里输入:Python:Select Interpreter...
C# Winform序列化和反序列化
在NET Framework 4.7.2中不能用Newtonsoft.Json进行序列化和反序列化,为解决此问题,采用System.Text.Json进行序列化,注意要添加System.Memory的引用。 1、创建测试类 using System; using System.Collections.Generic; using System.Linq; …...
crc原理概述
CRC(循环冗余校验)是一种错误检测技术,用于确保数据在传输或存储过程中没有发生变化。它通过将数据视为一个多项式,利用二进制除法得到一个校验码(CRC值)。接收方使用相同的算法验证数据和CRC值是否匹配&am…...
C++要求或禁止在堆中产生对象
有时你想这样管理某些对象,要让某种类型的对象能够自我销毁,也就是能够“delete this”。很明显这种管理方式需要此类型对象被分配在堆中。而其它一些时候你想获得一种保障:“不在堆中分配对象,从而保证某种类型的类不会发生内存泄…...
为什么阿里开发手册推荐用静态工厂方法代替构造器?
🍅 作者简介:哪吒,CSDN2021博客之星亚军🏆、新星计划导师✌、博客专家💪 🍅 哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师 🍅 技术交流:定期更新…...
前端写法建议【让项目更加易于维护】
背景 标题前提条件: 没有字典接口、或其他原因,需要前端手动维护的情况 示例环境:vue2,其他项目同理 示例 如果项目有某种类别,前端和后端约定好了,某些情况下,需要前端写死时。 比如有字段…...
EasyExcel 自定义转换器、自定义导出字典映射替换、满足条件内容增加样式,完整代码+详细注释说明
虽然最之前是在其他地方看到的,但最终因缘巧合下找到了原文,还是尊重一下原作者。 参考引用了这位佬的博客,确实方便使用。 https://blog.csdn.net/qq_45914616/article/details/137200688?spm1001.2014.3001.5502 这是一个基于Easyexcel通过…...
C语言学习笔记 Day10(指针--中)
Day10 内容梳理: 目录 Chapter 7 指针 7.4 指针 & 数组 (1)指针操作数组元素 (2)指针加减运算 1)加法 2)减法 (3)指针数组 7.5 多级指针 Chapter 7 指针 …...
网页显示打印 pdf
文件服务使用 minio,使用 nginx 反向代理。 将文件存放在 minio 上,如果是公开的文件,则统一放到一个桶,设置为公开只读。 如果是私有文件,则使用临时链接,给有权限的用户查看和打印。 要实现在 html 页…...
1948-2024.5金融许可信息明细数据
1948-2024.5金融许可信息明细数据 1、时间:1948-2024.5 2、指标:来源表、机构编码、机构名称、所属银行、机构类型、业务范围、机构住所、地理坐标、行政区划代码、所属区县、所属城市、所属省份、邮政编码、发证日期、批准日期、发证机关、流水号、是…...
【笔记】从零开始做一个精灵龙女-画贴图阶段(终)
这篇主要是细节,包括花纹和其它一些细化 皮肤 脖子 脖子一定要压暗,不然前后关系体现不出来 脸 1. 忘了有uv缝了,记得打开投影模式画 顺着头发轨迹长的方向画出发际线 背包手镯 1.先画出暗色花纹: 2.再加亮色,亮…...
从MySQL到Elasticsearch:创建酒店索引案例
在现代的数据管理中,Elasticsearch(简称ES)因其强大的搜索功能和灵活的索引结构而受到广泛欢迎。本篇博客将介绍如何根据MySQL数据库中的酒店表定义,创建一个相应的Elasticsearch索引。 MySQL与Elasticsearch的对比 在开始之前&…...
Webkit与Web Push API:提升用户体验的推送技术
Web Push API是一种允许网站向用户发送通知的Web技术,即使用户没有打开网站也能接收到信息。这项技术可以显著提升用户的参与度和满意度。Webkit,作为Safari和其他浏览器的内核,对Web Push API的支持情况如何?本文将深入探讨Web P…...
Java线程池的拒绝策略
在 Java 线程池中,常见的拒绝策略: AbortPolicy(中止策略) 特点:直接抛出 RejectedExecutionException 异常来拒绝新任务的提交。应用场景:适用于对系统的稳定性要求较高,不希望丢失任务&#…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
