当前位置: 首页 > news >正文

【Hot100】链表

这一类的核心思想是:指针操作的艺术

  • 虚拟头结点 (dummy):解决头结点可能被修改或删除的特殊情况,统一操作逻辑。
  • 双指针
    • 快慢指针:找中点、判环、找倒数第 N 个。
    • 前后指针:反转链表、删除节点。
  • 递归:将大问题分解为“处理当前节点 + 递归处理剩余链表”。
  • 辅助结构:优先队列(堆)用于合并 K 个链表。

📝 本类题型总结

题目 核心技巧 关键差异点 (vs 反转链表)
206. 反转链表 三指针迭代 基准prev, curr, next 轮流前移,反转指向。
21. 合并两链表 双指针 + Dummy 比较两指针值,小的接后。需 Dummy 头
141. 环形链表 快慢指针 fast 走 2 步,slow 走 1 步。相遇即有环
142. 环形入口 快慢指针 + 数学 相遇后,一指针回 head,同步走 1 步,再遇即入口
19. 删倒数 N 快慢指针 + Dummy fast 先走 n 步,同步走后 slow 指向 前驱
23. 合并 K 链表 优先队列 (堆) 维护 K 个头结点的最小堆,每次取最小,复杂度 $O(N \log K)$

🏆 母题:LeetCode 206. 反转链表 (Reverse Linked List)

题目内容:给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
核心逻辑

  1. 定义 prev = null, curr = head
  2. 遍历链表:
    • 暂存 nextTemp = curr.next
    • 反转指针curr.next = prev
    • 移动指针:prev = curr, curr = nextTemp
  3. 返回 prev(新的头结点)。
class Solution {public ListNode reverseList(ListNode head) {ListNode prev = null;ListNode curr = head;while (curr != null) {ListNode nextTemp = curr.next; // 1. 暂存下一个节点,防止断链curr.next = prev;              // 2. ⚠️ [核心逻辑] 反转当前节点的指针指向prev = curr;                   // 3. prev 前移curr = nextTemp;               // 4. curr 前移}return prev; // 循环结束时,curr 为 null,prev 指向新的头结点}
}

🔄 变体 1:LeetCode 21. 合并两个有序链表 (Merge Two Sorted Lists)

题目内容:将两个升序链表合并为一个新的 升序 链表并返回。
相比母题的变化

  1. 虚拟头结点:引入 dummy 节点,简化头结点的处理。
  2. 双指针比较:同时遍历 l1l2,比较当前值,将较小的节点接到结果链表后。
  3. 收尾:当一个链表遍历完后,直接将另一个链表剩余部分接上。
class Solution {public ListNode mergeTwoLists(ListNode l1, ListNode l2) {// ⚠️ [变化点 1] 使用虚拟头结点 dummy,避免处理头结点为空的特殊情况ListNode dummy = new ListNode(-1);ListNode curr = dummy;while (l1 != null && l2 != null) {// ⚠️ [变化点 2] 双指针比较,小的接在后面if (l1.val <= l2.val) {curr.next = l1;l1 = l1.next;} else {curr.next = l2;l2 = l2.next;}curr = curr.next;}// ⚠️ [变化点 3] 接上剩余部分(只有一个会非空)curr.next = (l1 == null) ? l2 : l1;return dummy.next;}
}

🔄 变体 2:LeetCode 141. 环形链表 (Linked List Cycle)

题目内容:给你一个链表的头节点 head ,判断链表中是否有环。
相比母题的变化

  1. 快慢指针
    • slow 每次走 1 步,fast 每次走 2 步。
    • 如果有环,fast 最终会追上 slow(相遇)。
    • 如果无环,fast 会先到达 null
public class Solution {public boolean hasCycle(ListNode head) {if (head == null || head.next == null) return false;ListNode slow = head;ListNode fast = head;// ⚠️ [变化点 1] 快指针走两步,需确保 fast 和 fast.next 都不为空while (fast != null && fast.next != null) {slow = slow.next;          // 走 1 步fast = fast.next.next;     // 走 2 步if (slow == fast) {return true; // ⚠️ [核心逻辑] 相遇说明有环}}return false; // fast 到达末尾,无环}
}

🔄 变体 3:LeetCode 142. 环形链表 II (Linked List Cycle II)

题目内容:给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
相比母题的变化

  1. 数学推导
    • 第一阶段:快慢指针相遇(同 141 题)。
    • 第二阶段:将一个指针(如 slow)重置回 head,另一个指针(fast)留在相遇点。
    • 两个指针每次都走 1 步,再次相遇的点即为 入环点
public class Solution {public ListNode detectCycle(ListNode head) {if (head == null || head.next == null) return null;ListNode slow = head;ListNode fast = head;boolean hasCycle = false;// 1. 判断是否有环并找到相遇点while (fast != null && fast.next != null) {slow = slow.next;fast = fast.next.next;if (slow == fast) {hasCycle = true;break;}}if (!hasCycle) return null;// ⚠️ [变化点 2] 寻找入环点// 将 slow 重置回 head,fast 保持在相遇点slow = head;while (slow != fast) {slow = slow.next;fast = fast.next;}// 再次相遇点即为入环点return slow;}
}

🔄 变体 4:LeetCode 19. 删除链表的倒数第 N 个结点 (Remove Nth Node From End)

题目内容:给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
相比母题的变化

  1. 虚拟头结点:必须使用 dummy,因为可能要删除头结点。
  2. 快慢指针间距
    • fast 先走 n 步。
    • 然后 slowfast 同时走,直到 fast 到达末尾 (null)。
    • 此时 slow 指向 待删除节点的前驱节点
  3. 删除操作slow.next = slow.next.next
class Solution {public ListNode removeNthFromEnd(ListNode head, int n) {// ⚠️ [变化点 1] 必须使用 dummy,防止删除的是头结点ListNode dummy = new ListNode(0, head);ListNode slow = dummy;ListNode fast = head;// ⚠️ [变化点 2] fast 先走 n 步for (int i = 0; i < n; i++) {fast = fast.next;}// 同时移动,直到 fast 到达末尾while (fast != null) {slow = slow.next;fast = fast.next;}// ⚠️ [变化点 3] 此时 slow 指向待删节点的前驱slow.next = slow.next.next;return dummy.next;}
}

🔄 变体 5:LeetCode 23. 合并 K 个升序链表 (Merge k Sorted Lists)

题目内容:给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。
相比母题的变化

  1. 数据结构:使用 优先队列 (最小堆)
  2. 逻辑
    • 将所有链表的 头结点 放入最小堆。
    • 每次从堆中取出 值最小 的节点,接到结果链表后。
    • 如果该节点有 next,将 next 放入堆中。
    • 重复直到堆空。
  3. 复杂度:$O(N \log K)$,其中 $N$ 是总节点数,$K$ 是链表个数。
import java.util.PriorityQueue;class Solution {public ListNode mergeKLists(ListNode[] lists) {if (lists == null || lists.length == 0) return null;// ⚠️ [变化点 1] 定义最小堆,比较器按节点值排序PriorityQueue<ListNode> pq = new PriorityQueue<>((a, b) -> a.val - b.val);// 将所有非空链表的头结点加入堆for (ListNode node : lists) {if (node != null) {pq.offer(node);}}ListNode dummy = new ListNode(-1);ListNode curr = dummy;while (!pq.isEmpty()) {// ⚠️ [变化点 2] 取出当前最小节点ListNode minNode = pq.poll();curr.next = minNode;curr = curr.next;// 如果该节点有后继,加入堆if (minNode.next != null) {pq.offer(minNode.next);}}return dummy.next;}
}
http://www.jsqmd.com/news/451441/

相关文章:

  • 零基础掌握AutoDock Vina:分子对接完整工作流指南
  • 3.8-1
  • AI协同编程:在快马平台中让Codex与其他模型配合,智能生成与优化API代码
  • DeOldify图像上色实战教程:Python环境快速部署与模型调用
  • 高效构建企业级虚拟桌面环境:PVE-VDIClient全面应用指南
  • 实测AnythingtoRealCharacters2511:日漫、美漫角色一键真人化,效果超乎想象
  • MedGemma X-Ray部署教程:国产昇腾/寒武纪平台适配可行性验证
  • NoFences:颠覆式桌面分区管理工具,让数字空间重获秩序
  • CHORD-X与ComfyUI工作流结合:可视化构建复杂视频分析流程
  • Qwen3-0.6B-FP8在教育场景落地:开发AI编程作业批改助手
  • ChatGLM3-6B效果实测:对比云端API,本地推理的隐私与速度优势
  • 手把手教你理解SVM和集成学习:从理论推导到实际应用(附BUAA考试真题解析)
  • 如何通过applera1n实现iOS设备激活锁解除:从困境到解决方案的创新路径
  • 基于OFA-Image-Caption的智能相册管理系统:JavaScript实现图像检索与分类
  • Qwen3-ASR-0.6B智能硬件开发:RaspberryPi语音控制套件
  • GLM-ASR-Nano-2512保姆级教程:safetensors模型加载与tokenizer配置
  • Nano-Banana实战教程:与Fusion360联动实现设计-拆解-文档一体化
  • YOLO12开源可部署优势解析:本地权重加载规避网络依赖与版本风险
  • IndexTTS2 V23在短视频配音中的应用:快速生成带情绪的旁白和对话
  • 从零开始训练人脸识别模型:Face Analysis WebUI全流程
  • Qwen3-ForcedAligner实战:如何将长音频剧本快速转换为带时间轴的字幕?
  • LiuJuan20260223Zimage赋能微信小程序开发:智能客服对话生成实战
  • 避坑指南:ArcGIS批量克里金插值常见问题与解决方案(含数据预处理建议)
  • Qwen3-ASR-0.6B语音特征分析与可视化:MATLAB算法仿真教程
  • OneNote Md Exporter:高效转换与跨平台兼容的OneNote笔记导出工具
  • iOS设备激活锁如何破解?AppleRa1n工具全解析与实战指南
  • 4个维度掌握PYPOWER:电力系统仿真开源工具工程应用实战指南
  • lychee-rerank-mm保姆级入门:3步搞定图文内容相关性打分
  • RVC模型服务器端高可用部署:Ubuntu系统下的Docker与Kubernetes实践
  • YOLO12 OBB检测实战:倾斜目标检测在无人机巡检中的应用案例