返回

LeetCode 第 21 号问题:合并两个有序链表的进阶秘笈

见解分享

算法策略

在合并两个有序链表时,我们常见的算法策略有:

  • 迭代合并:逐个比较两个链表的节点,将较小的节点添加到新的有序链表中。重复此过程,直到其中一个链表为空。
  • 递归合并:将问题分解为子问题,递归地合并两个链表的子链表,然后将合并后的子链表连接起来。
  • 分治合并:将两个链表分成更小的子链表,递归地合并这些子链表,然后将合并后的子链表连接起来。

这三种策略都各有优缺点,具体选择哪种策略取决于具体情况。例如,迭代合并算法简单易懂,但时间复杂度为 O(n),其中 n 为两个链表的总长度。递归合并算法的时间复杂度为 O(log n),但空间复杂度为 O(log n)。分治合并算法的时间复杂度为 O(log n),空间复杂度为 O(1)。

优化方案

为了优化合并两个有序链表的算法,我们可以采用以下方案:

  • 使用快速指针:在迭代合并算法中,我们通常使用两个指针来比较两个链表的节点。我们可以使用快速指针来代替一个指针,这样可以将时间复杂度从 O(n) 减少到 O(n/2)。
  • 使用栈或队列:在递归合并算法中,我们可以使用栈或队列来保存需要合并的子链表。这样可以避免递归调用栈的开销,从而提高算法的效率。
  • 使用分治合并算法:分治合并算法的时间复杂度为 O(log n),是三种合并算法中最优的。我们可以使用分治合并算法来合并两个有序链表,从而获得最佳的性能。

代码实现

def merge_two_sorted_lists(l1, l2):
    """
    合并两个有序链表

    Args:
        l1: 第一个有序链表
        l2: 第二个有序链表

    Returns:
        合并后的有序链表
    """

    # 创建一个新的链表来保存合并后的结果
    result = ListNode(0)

    # 设置一个指针指向新的链表的尾部
    tail = result

    # 循环比较两个链表的节点
    while l1 and l2:
        # 如果 l1 的节点值小于或等于 l2 的节点值
        if l1.val <= l2.val:
            # 将 l1 的节点添加到新的链表中
            tail.next = l1
            # 将 l1 的指针指向下一个节点
            l1 = l1.next
        # 否则
        else:
            # 将 l2 的节点添加到新的链表中
            tail.next = l2
            # 将 l2 的指针指向下一个节点
            l2 = l2.next

        # 将尾部指针指向新的节点
        tail = tail.next

    # 将剩余的节点添加到新的链表中
    if l1:
        tail.next = l1
    if l2:
        tail.next = l2

    # 返回合并后的链表
    return result.next

复杂度分析

合并两个有序链表的时间复杂度取决于所使用的算法。迭代合并算法的时间复杂度为 O(n),其中 n 为两个链表的总长度。递归合并算法的时间复杂度为 O(log n),空间复杂度为 O(log n)。分治合并算法的时间复杂度为 O(log n),空间复杂度为 O(1)。

在实际应用中,我们通常使用迭代合并算法来合并两个有序链表,因为该算法简单易懂,并且在大多数情况下具有较好的性能。如果两个链表非常长,我们也可以考虑使用递归合并算法或分治合并算法来获得更好的性能。