从拉链到链表:探索有序链表的合并之道

生活中的合并

想象你正在整理两叠按日期排好序的收据。最自然的方式就是:拿起两叠收据,每次比较最上面的日期,选择日期较早的那张放入新的一叠中。这个简单的日常操作,恰恰就是我们今天要讨论的有序链表合并问题的真实写照。

问题描述

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),只需要几个指针
  • 优点:充分利用了输入链表已排序的特性

技巧与思考

  1. 哨兵节点的妙用

    • 使用哨兵节点可以统一边界情况处理
    • 避免了对头节点的特殊处理
  2. 就地合并的思想

    • 不需要创建新节点
    • 通过改变指针指向来实现合并
  3. 处理剩余节点

    • 直接连接剩余链表
    • 避免了继续遍历剩余节点

实际应用延伸

合并有序链表的思想在实际开发中很常见:

  1. 数据库中的有序结果集合并
  2. 文件系统中有序文件的合并
  3. 日志系统中按时间戳排序的日志合并

小结

合并有序链表看似简单,实则蕴含着重要的算法思想:

  1. 如何高效处理有序数据
  2. 指针操作的技巧
  3. 如何简化边界条件处理

记住:当遇到类似的合并问题时,先考虑数据是否有序,如果有序,往往可以设计出更优雅高效的解法。


作者:忍者算法

公众号:忍者算法

我准备了一份刷题清单,以及这些题目的详细题解,覆盖了绝大部分常见面试题。我可以很负责任地说,只要你把这些题真正掌握了,80%的算法面试都能遇到相似题目。公众号回复【刷题清单】获取~

【忍者算法】从拉链到链表:探索有序链表的合并之道|LeetCode 21 合并两个有序链表的更多相关文章

  1. [LeetCode] 21. 合并两个有序链表

    题目链接:https://leetcode-cn.com/problems/merge-two-sorted-lists/ 题目描述: 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定 ...

  2. LeetCode 21 ——合并两个有序链表

    1. 题目 2. 解答 新建一个带有哨兵结点的链表,依次比较两个有序链表的结点值,将较小值的结点插入到新链表后面.直到其中一个比较完毕,将另一个链表剩余的结点全部放到新链表最后面即可.最后,可以删除哨 ...

  3. <每日 1 OJ> -LeetCode 21. 合并两个有序链表

    题目: 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4输出:1->1-> ...

  4. LeetCode 21. 合并两个有序链表(Merge Two Sorted Lists)

    21. 合并两个有序链表 21. Merge Two Sorted Lists 题目描述 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. LeetCode ...

  5. LeetCode 21. 合并两个有序链表(Merge Two Sorted Lists)

    题目描述 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4 输出:1->1-& ...

  6. LeetCode 21. 合并两个有序链表(Python)

    题目: 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4 输出:1->1-&g ...

  7. Java实现 LeetCode 21 合并两个有序链表

    21. 合并两个有序链表 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4 输出:1 ...

  8. 力扣Leetcode 21. 合并两个有序链表

    合并两个有序链表 将两个升序链表合并为一个新的升序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4 输出:1-> ...

  9. [LeetCode]21. 合并两个有序链表(递归)

    题目 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4 输出:1->1-> ...

  10. [LeetCode]21.合并两个有序链表(Java)

    原题地址: merge-two-sorted-lists 题目描述: 将两个升序链表合并为一个新的 升序 链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例 1: 输入:l1 = [1 ...

随机推荐

  1. Echarts 图例组件

    1.图例组件的基本介绍 图例组件legend展现了不同系列的标记(symbol),颜色和名字.可以通过点击图例控制哪些系列不显示. 2.开启图例组件 开启图例组件默认时关闭状态,通过配置legend字 ...

  2. DDCA —— 内存一致性

    1. 同步(Synchronization) 1.1 构造锁(Locks) 原子(atomic)执行:应用程序的某些部分必须独占执行(原子性),这意味着在这些部分执行期间,其他并行进程无法访问或修改相 ...

  3. 【软件配置】使用 brew 安装特定版本软件

    目录 使用 brew 安装特定版本软件 背景 方法一:直接安装 方法二:利用历史的 rb 文件安装 参考资料 使用 brew 安装特定版本软件 背景 brew 是 Mac 下非常好用的包管理工具,可以 ...

  4. 史上最牛X的微信公众号!

    今天给大家推荐一个最牛逼的微信公众号,专注分享技术干货,包括但不限于Java,HTML,JavaScript,MySQL,Redis,Windows,Linux,Spring,SpringBoot,V ...

  5. R数据分析:临床研究样本量计算、结果解读与实操

    很久之前给大家写过一篇文章详细介绍了样本量计算的底层逻辑,不过那篇文章原理是依照卡方比较来写的,可以拓展到均值比较,但视角还是比较小,今天从整个临床研究的角度结合具体的例子谈谈大家遇到的样本量的计算方 ...

  6. 使用 OpenXML 创建第一个 Word 文档

    using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Wordprocessing; public class Op ...

  7. 中电资讯 - 一路“标”升,喜迎Q3开门红

    Q2收获满满,Q3精彩再启! 近日,中电金信多项业务取得新进展 接连中标多个项目 中电金信数字科技集团旗下优智汇咨询中标浙江民泰商业银行信息科技战略发展规划(2022-2025)项目.优智汇将根据民泰 ...

  8. AI视频抠图来了!还可以替换视频背景,附下载链接

    虽然人工智能正在飞速发展中,图像处理技术也在不断升级,但视频背景去除一直都是图像处理任务中最具挑战性的难题之一 Clipper是一款专注于高精度图像分割的AI工具,用于图像和视频的背景去除,允许用户直 ...

  9. Qt编写安防视频监控系统33-onvif云台控制

    一.前言 云台控制也是onvif功能中最常用的,最常用的功能排第一的是拿到视频流地址,排第二的就是云台控制了,云台控制的含义就是对带云台的摄像机进行上下左右的移动,一般云台摄像机都是带有一个小电机,一 ...

  10. DVWA靶场Brute Force (暴力破解) 漏洞low(低),medium(中等),high(高),impossible(不可能的)所有级别通关教程及代码审计

    暴力破解 暴力破解是一种尝试通过穷尽所有可能的选项来获取密码.密钥或其他安全凭证的攻击方法.它是一种简单但通常无效率的破解技术,适用于密码强度较弱的环境或当攻击者没有其他信息可供利用时.暴力破解的基本 ...