LeetCode 23. 合并 K 个升序链表(分治 + 链表归并)

LeetCode 23. 合并 K 个升序链表(分治 + 链表归并)

一、题目描述

给你一个链表数组,每个链表都已经按照升序排列,请将所有链表合并成一条升序链表,并返回合并后的链表头节点。

示例

python 复制代码
输入:
lists = [[1,4,5],[1,3,4],[2,6]]

输出:
[1,1,2,3,4,4,5,6]

二、最容易想到的方法

最直接的方法是顺序合并:

text 复制代码
先合并第1条和第2条链表
再和第3条链表合并
再和第4条链表合并......

假设:

  • 一共有 k 条链表
  • 每条链表平均长度为 n

时间复杂度:

text 复制代码
O(k²n)

当链表数量较多时,效率比较低。


三、最优思路:分治(归并排序思想)

归并排序的核心思想:

大问题拆成小问题,小问题解决后再合并。

例如:

text 复制代码
L1 L2 L3 L4 L5 L6

先拆成:

text 复制代码
L1 L2 L3      L4 L5 L6

继续拆:

text 复制代码
L1   L2 L3      L4   L5 L6

最后不断合并:

text 复制代码
(L1+L2) + (L3)
(L4+L5) + (L6)

最终得到整个结果。


四、子问题:合并两个升序链表

这是经典题《合并两个有序链表》的做法。

核心思路

  • 两个指针分别指向两条链表;
  • 每次选择较小节点接到结果链表后面;
  • 某条链表遍历结束后,直接把另一条链表剩余部分接到尾部。

代码

python 复制代码
def mergeTwoLists(l1, l2):
    dummy = ListNode()
    cur = dummy

    while l1 and l2:
        if l1.val < l2.val:
            cur.next = l1
            l1 = l1.next
        else:
            cur.next = l2
            l2 = l2.next

        cur = cur.next

    if l1:
        cur.next = l1
    else:
        cur.next = l2

    return dummy.next

五、分治递归

定义递归函数:

python 复制代码
merge(left, right)

表示:

合并 lists[left:right+1] 区间内所有链表。

终止条件

python 复制代码
if left == right:
    return lists[left]

递归拆分

python 复制代码
mid = (left + right) // 2

left_list = merge(left, mid)
right_list = merge(mid + 1, right)

return mergeTwoLists(left_list, right_list)

六、完整代码

python 复制代码
from typing import List, Optional

class Solution:
    def mergeKLists(
        self,
        lists: List[Optional[ListNode]]
    ) -> Optional[ListNode]:

        if not lists:
            return None

        def mergeTwoLists(l1, l2):
            dummy = ListNode()
            cur = dummy

            while l1 and l2:
                if l1.val < l2.val:
                    cur.next = l1
                    l1 = l1.next
                else:
                    cur.next = l2
                    l2 = l2.next

                cur = cur.next

            if l1:
                cur.next = l1
            else:
                cur.next = l2

            return dummy.next

        def merge(left, right):
            if left == right:
                return lists[left]

            mid = (left + right) // 2

            left_list = merge(left, mid)
            right_list = merge(mid + 1, right)

            return mergeTwoLists(
                left_list,
                right_list
            )

        return merge(0, len(lists) - 1)

七、复杂度分析

时间复杂度

text 复制代码
O(kn × logk)

每一层需要遍历所有节点,共有 logk 层。

空间复杂度

text 复制代码
O(logk)

主要来自递归调用栈。


八、高频易错点

1、返回值写错

错误:

python 复制代码
return dummy

正确:

python 复制代码
return dummy.next

2、参数类型搞混

python 复制代码
merge()

接收的是数组下标。

python 复制代码
mergeTwoLists()

接收的是链表头节点。


3、忘记接收递归返回值

错误:

python 复制代码
merge(left, mid)
merge(mid + 1, right)

正确:

python 复制代码
left_list = merge(left, mid)
right_list = merge(mid + 1, right)

九、一句话总结

合并 K 个链表的本质,就是利用分治思想,把问题不断拆成「合并两个有序链表」。