(LeetCode-Hot100)21. 合并两个有序链表

合并两个有序链表

问题简介

🔗 LeetCode 21. 合并两个有序链表

📝 题目描述

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。


🧪 示例说明

示例 1:

复制代码
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

复制代码
输入:l1 = [], l2 = []
输出:[]

示例 3:

复制代码
输入:l1 = [], l2 = [0]
输出:[0]

💡 解题思路

✅ 方法一:递归法(推荐)

核心思想

比较两个链表头节点的值,较小者作为合并后链表的头节点,然后递归处理剩余部分。

步骤如下

  1. 如果 l1 为空,直接返回 l2
  2. 如果 l2 为空,直接返回 l1
  3. l1.val <= l2.val,则 l1.next = mergeTwoLists(l1.next, l2)
  4. 否则,l2.next = mergeTwoLists(l1, l2.next)
  5. 返回较小节点作为当前头节点。

优点:代码简洁,逻辑清晰。

缺点:递归深度可能较大(最坏 O(m+n)),在极端情况下可能导致栈溢出(但 LeetCode 测试用例通常不会触发)。


✅ 方法二:迭代法(双指针)

核心思想

使用一个虚拟头节点(dummy node)简化边界处理,用两个指针分别遍历两个链表,每次选择较小节点连接到结果链表。

步骤如下

  1. 创建虚拟头节点 dummy 和当前指针 cur = dummy
  2. l1l2 都非空时:
    • l1.val <= l2.val,则 cur.next = l1l1 = l1.next
    • 否则 cur.next = l2l2 = l2.next
    • cur = cur.next
  3. 循环结束后,将未遍历完的链表(l1l2)接到 cur.next
  4. 返回 dummy.next

优点:空间复杂度 O(1),无递归开销。

缺点:代码略长,需注意指针操作。


💻 代码实现

java:Java 复制代码
// 方法一:递归
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if (list1 == null) return list2;
        if (list2 == null) return list1;
        if (list1.val <= list2.val) {
            list1.next = mergeTwoLists(list1.next, list2);
            return list1;
        } else {
            list2.next = mergeTwoLists(list1, list2.next);
            return list2;
        }
    }
}

// 方法二:迭代(双指针 + 虚拟头节点)
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode dummy = new ListNode(-1);
        ListNode cur = dummy;
        
        while (list1 != null && list2 != null) {
            if (list1.val <= list2.val) {
                cur.next = list1;
                list1 = list1.next;
            } else {
                cur.next = list2;
                list2 = list2.next;
            }
            cur = cur.next;
        }
        
        // 接上剩余部分
        cur.next = (list1 != null) ? list1 : list2;
        
        return dummy.next;
    }
}
go:Go 复制代码
// 方法一:递归
func mergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode {
    if list1 == nil {
        return list2
    }
    if list2 == nil {
        return list1
    }
    if list1.Val <= list2.Val {
        list1.Next = mergeTwoLists(list1.Next, list2)
        return list1
    } else {
        list2.Next = mergeTwoLists(list1, list2.Next)
        return list2
    }
}

// 方法二:迭代
func mergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode {
    dummy := &ListNode{Val: -1}
    cur := dummy

    for list1 != nil && list2 != nil {
        if list1.Val <= list2.Val {
            cur.Next = list1
            list1 = list1.Next
        } else {
            cur.Next = list2
            list2 = list2.Next
        }
        cur = cur.Next
    }

    if list1 != nil {
        cur.Next = list1
    } else {
        cur.Next = list2
    }

    return dummy.Next
}

🎯 示例演示(以示例1为例)

初始状态:

复制代码
l1: 1 → 2 → 4
l2: 1 → 3 → 4

迭代过程(方法二):

步骤 cur.next l1 l2 结果链表(dummy→...)
0 - 1→2→4 1→3→4 -1
1 1 (l2) 1→2→4 3→4 -1 → 1
2 1 (l1) 2→4 3→4 -1 → 1 → 1
3 2 4 3→4 ... → 1 → 2
4 3 4 4 ... → 2 → 3
5 4 (l1) nil 4 ... → 3 → 4
6 4 (l2) nil nil ... → 4 → 4

最终返回 dummy.next1→1→2→3→4→4


✅ 答案有效性证明

我们需证明合并后的链表满足:

  1. 包含所有原节点 :两种方法均遍历了 l1l2 的所有节点,且未跳过任何节点;
  2. 保持升序:每一步都选择当前最小节点,由数学归纳法可知整体有序;
  3. 正确终止:当任一链表为空时,直接拼接另一链表(其本身有序),保证完整性。

因此,算法正确。


📊 复杂度分析

方法 时间复杂度 空间复杂度 说明
递归 O(m + n) O(m + n) 递归调用栈深度为 m+n
迭代 O(m + n) O(1) 仅使用常数额外空间

其中 mn 分别为两个链表的长度。


📌 问题总结

  • 关键技巧
    • 递归:将大问题分解为"选头 + 合并剩余";
    • 迭代:使用 虚拟头节点(dummy) 避免处理空链表的边界情况。
  • 适用场景
    • 链表合并是归并排序的核心步骤;
    • 在多路归并、K个有序链表合并等问题中会复用此思想。
  • 延伸思考
    • 若扩展为合并 K 个有序链表,可使用优先队列(堆)优化。

💡 建议:面试中优先写迭代解法(空间更优),若时间充裕可补充递归思路展示思维广度。

github地址: https://github.com/swf2020/LeetCode-Hot100-Solutions

相关推荐
计算机安禾2 分钟前
【数据结构与算法】第22篇:线索二叉树(Threaded Binary Tree)
c语言·开发语言·数据结构·学习·算法·链表·visual studio code
算法鑫探16 分钟前
解密2025数字密码:数位统计之谜
c语言·数据结构·算法·新人首发
:mnong21 分钟前
Superpowers 项目设计分析
java·c语言·c++·python·c#·php·skills
计算机安禾25 分钟前
【数据结构与算法】第21篇:二叉树遍历的经典问题:由遍历序列重构二叉树
c语言·数据结构·学习·算法·重构·visual studio code·visual studio
信奥胡老师36 分钟前
P1255 数楼梯
开发语言·数据结构·c++·学习·算法
扶苏-su1 小时前
Java--获取 Class 类对象
java·开发语言
东离与糖宝1 小时前
LangChain4j vs Spring AI:最新对比,Java企业级Agent开发
java·人工智能
96771 小时前
C++多线程2 如何优雅地锁门 (lock_guard) 多线程里的锁的种类
java·开发语言·c++
老衲提灯找美女1 小时前
数据库事务
java·大数据·数据库
爱睡懒觉的焦糖玛奇朵1 小时前
【工业级落地算法之人员摔倒检测算法详解】
人工智能·python·深度学习·神经网络·算法·yolo·目标检测