【忍者算法】从拉链到链表:探索有序链表的合并之道|LeetCode 21 合并两个有序链表
从拉链到链表:探索有序链表的合并之道
生活中的合并
想象你正在整理两叠按日期排好序的收据。最自然的方式就是:拿起两叠收据,每次比较最上面的日期,选择日期较早的那张放入新的一叠中。这个简单的日常操作,恰恰就是我们今天要讨论的有序链表合并问题的真实写照。
问题描述
LeetCode第21题"合并两个有序链表"要求:将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
例如:
输入:1 → 2 → 4, 1 → 3 → 4
输出:1 → 1 → 2 → 3 → 4 → 4
输入:空链表, 0
输出:0
输入:空链表, 空链表
输出:空链表
暴力解法:转换为数组排序
最直观的想法可能是:把两个链表的值都放到一个数组里,排序后再创建新链表。这种方法虽然不够优雅,但对于理解问题很有帮助。
暴力解法实现
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
// 创建数组存储所有节点值
List<Integer> values = new ArrayList<>();
// 遍历第一个链表
while (list1 != null) {
values.add(list1.val);
list1 = list1.next;
}
// 遍历第二个链表
while (list2 != null) {
values.add(list2.val);
list2 = list2.next;
}
// 排序数组
Collections.sort(values);
// 构建新链表
ListNode dummy = new ListNode(0); // 哨兵节点
ListNode current = dummy;
// 根据排序后的数组创建新链表
for (int value : values) {
current.next = new ListNode(value);
current = current.next;
}
return dummy.next;
}
优化解法:双指针遍历
既然输入的链表已经排好序,我们完全可以模拟整理收据的过程:同时遍历两个链表,每次选择较小的节点连接到结果链表中。这就像拉链一样,将两个有序序列合并成一个。
代码实现与详解
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
// 创建哨兵节点,简化边界情况处理
ListNode dummy = new ListNode(0);
ListNode current = dummy;
// 当两个链表都不为空时,比较并连接
while (list1 != null && list2 != null) {
if (list1.val <= list2.val) {
current.next = list1;
list1 = list1.next;
} else {
current.next = list2;
list2 = list2.next;
}
current = current.next;
}
// 处理剩余节点
if (list1 != null) {
current.next = list1;
}
if (list2 != null) {
current.next = list2;
}
return dummy.next;
}
图解过程
1) 初始状态:
list1: 1 → 2 → 4
list2: 1 → 3 → 4
result: dummy →
2) 第一次比较后:
list1: 2 → 4
list2: 1 → 3 → 4
result: dummy → 1 →
3) 第二次比较后:
list1: 2 → 4
list2: 3 → 4
result: dummy → 1 → 1 →
4) 最终结果:
result: dummy → 1 → 1 → 2 → 3 → 4 → 4
复杂度比较
暴力解法:
- 时间复杂度:O(nlogn),主要来自排序过程
- 空间复杂度:O(n),需要额外数组存储所有节点
- 缺点:没有利用链表已排序的特性
双指针解法:
- 时间复杂度:O(n),只需要遍历一次
- 空间复杂度:O(1),只需要几个指针
- 优点:充分利用了输入链表已排序的特性
技巧与思考
哨兵节点的妙用
- 使用哨兵节点可以统一边界情况处理
- 避免了对头节点的特殊处理
就地合并的思想
- 不需要创建新节点
- 通过改变指针指向来实现合并
处理剩余节点
- 直接连接剩余链表
- 避免了继续遍历剩余节点
实际应用延伸
合并有序链表的思想在实际开发中很常见:
- 数据库中的有序结果集合并
- 文件系统中有序文件的合并
- 日志系统中按时间戳排序的日志合并
小结
合并有序链表看似简单,实则蕴含着重要的算法思想:
- 如何高效处理有序数据
- 指针操作的技巧
- 如何简化边界条件处理
记住:当遇到类似的合并问题时,先考虑数据是否有序,如果有序,往往可以设计出更优雅高效的解法。
作者:忍者算法
公众号:忍者算法
我准备了一份刷题清单,以及这些题目的详细题解,覆盖了绝大部分常见面试题。我可以很负责任地说,只要你把这些题真正掌握了,80%的算法面试都能遇到相似题目。公众号回复【刷题清单】获取~
【忍者算法】从拉链到链表:探索有序链表的合并之道|LeetCode 21 合并两个有序链表的更多相关文章
- [LeetCode] 21. 合并两个有序链表
题目链接:https://leetcode-cn.com/problems/merge-two-sorted-lists/ 题目描述: 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定 ...
- LeetCode 21 ——合并两个有序链表
1. 题目 2. 解答 新建一个带有哨兵结点的链表,依次比较两个有序链表的结点值,将较小值的结点插入到新链表后面.直到其中一个比较完毕,将另一个链表剩余的结点全部放到新链表最后面即可.最后,可以删除哨 ...
- <每日 1 OJ> -LeetCode 21. 合并两个有序链表
题目: 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4输出:1->1-> ...
- LeetCode 21. 合并两个有序链表(Merge Two Sorted Lists)
21. 合并两个有序链表 21. Merge Two Sorted Lists 题目描述 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. LeetCode ...
- LeetCode 21. 合并两个有序链表(Merge Two Sorted Lists)
题目描述 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4 输出:1->1-& ...
- LeetCode 21. 合并两个有序链表(Python)
题目: 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4 输出:1->1-&g ...
- Java实现 LeetCode 21 合并两个有序链表
21. 合并两个有序链表 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4 输出:1 ...
- 力扣Leetcode 21. 合并两个有序链表
合并两个有序链表 将两个升序链表合并为一个新的升序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4 输出:1-> ...
- [LeetCode]21. 合并两个有序链表(递归)
题目 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4 输出:1->1-> ...
- [LeetCode]21.合并两个有序链表(Java)
原题地址: merge-two-sorted-lists 题目描述: 将两个升序链表合并为一个新的 升序 链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例 1: 输入:l1 = [1 ...
随机推荐
- three.js优化
Three js 开发的一些知识整理,方便后期遇到类似的问题,能够及时查阅使用. three.js 性能优化方面,整理一下常用的优化方法或者方向,供大家一个优化思考的方向 尽量重用Material和G ...
- cve-2021-3156-sudo堆溢出简单分析
调试方式 首先从github下载代码 https://github.com/sudo-project/sudo/archive/SUDO_1_9_5p1.tar.gz 编译 tar xf sudo-S ...
- Fuzz技术综述与文件Fuzz
文章一开始发表在微信公众号 https://mp.weixin.qq.com/s?__biz=MzUyNzc4Mzk3MQ==&mid=2247486189&idx=1&sn= ...
- Shiro简单入门+个人理解
身为一个刚刚进入开发行业的学生,进入公司就开始了Shiro框架的应用,特此在这里写下收获. Shiro是apache旗下一个开源安全框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权 ...
- Linux系统部署FineReport
1. 概述 1.1 应用场景 帆软提供 Linux 操作系统下可直接安装使用的 FineReport 设计器,满足不同系统的用户的操作需求. 支持中标麒麟.银河麒麟.UOS 的 Linux 操作系统 ...
- 中电金信:The Financial-Grade Digital Infrastructure
01 Product Introduction The Financial-Grade Digital Infrastructure is a digitally-enabled foundati ...
- Ubuntu 22.04 LTS 代号已经公布:那就是 Jammy Jellyfish
Ubuntu 22.04 LTS 代号已在 Ubuntu 开发之家 Launchpad 上公布. 在字母系列中的字母"I"之后,是"J". 因此,Canonic ...
- 解决springboot配置@ControllerAdvice不能捕获NoHandlerFoundException问题
使用springboot开发一个RESTful API服务,配置了@ControllerAdvice,其它类型异常都能正常捕获,就是不能捕获NoHandlerFoundException, 安装以往使 ...
- 深入理解 Servlet:从基础概念到高级特性与实战应用
一.Servlet简介与工作原理 Servlet是Java Web开发中的重要组件,它运行在服务器端,用于处理客户端的请求并返回响应.其工作原理涉及多个组件和步骤,从客户端发起请求到服务器端的处理和响 ...
- 基于开源IM即时通讯框架MobileIMSDK:RainbowChat-iOS端v9.1版已发布
关于MobileIMSDK MobileIMSDK 是一套专门为移动端开发的开源IM即时通讯框架,超轻量级.高度提炼,一套API优雅支持 UDP .TCP .WebSocket 三种协议,支持 iOS ...