在问 · 算法题解

21. 合并两个有序链表

简单链表哑节点 + 迭代合并力扣原题 ↗

题目描述

输入两个按非递减顺序排列的链表 list1 和 list2,链表用数组表示。

把两个链表的所有节点接成一个新链表,仍保持非递减顺序,返回新链表的头节点。新链表直接由原有节点拼接而成。结果为空时返回 null。

示例 1
输入:list1 = [1,2,4], list2 = [1,3,4]
输出:[1,1,2,3,4,4]

解释:按大小交替取两个链表的节点,得到 [1,1,2,3,4,4]。

示例 2
输入:list1 = [], list2 = []
输出:[]

解释:两个链表都为空,结果也为空。

示例 3
输入:list1 = [], list2 = [0]
输出:[0]

解释:一个为空,结果就是另一个链表。

数据范围

  • 两个链表的节点数都在 [0, 50] 之间
  • -100 <= Node.val <= 100
  • list1 和 list2 都已按非递减顺序排好。

解题思路:哑节点 + 迭代合并

用哑节点保存头部,每次接入两个当前节点中较小者,最后接上剩余链表。

为什么正确

tail 之前已经合并的前缀有序,且包含所有已经取出的节点。

复杂度分析

时间 O(m+n),额外空间 O(1)。

易错点

返回 dummy.next;不要返回最后的 tail。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def mergeTwoLists(self, list1, list2):
        dummy = ListNode(0)
        tail = dummy
        while list1 and list2:
            if list1.val <= list2.val:
                tail.next = list1
                list1 = list1.next
            else:
                tail.next = list2
                list2 = list2.next
            tail = tail.next
        tail.next = list1 or list2
        return dummy.next

载入编辑器,自己跑一遍 →

其他解法

同一种归并思想的两种实现:迭代版用哑节点和尾指针逐个接,空间 O(1);递归版更简洁,但占用栈空间。

解法 2:递归合并 时间 O(m+n),空间 O(m+n)

若其中一个链表为空,直接返回另一个。否则取两个头节点中较小的那个,把它的 next 设为「其余部分递归合并的结果」,然后返回它。

定义式的写法,代码极短,是理解链表递归的好例子。递归深度为 m+n,需要 O(m+n) 栈空间,链表很长时可能栈溢出。

class Solution:
    def mergeTwoLists(self, list1, list2):
        if not list1:
            return list2
        if not list2:
            return list1
        if list1.val <= list2.val:
            list1.next = self.mergeTwoLists(list1.next, list2)
            return list1
        list2.next = self.mergeTwoLists(list1, list2.next)
        return list2

链表 专题的其他题目

查看 链表 全部题目 →