返回

《剑指offer》—— 合并两个排序的链表,轻松掌握

前端

在编程的世界里,数据结构和算法是程序员的基本功。无论是构建应用程序还是解决复杂问题,都离不开数据结构和算法的支持。今天,我们就来聊聊如何合并两个已经排序的链表。

两个已经排序的链表,如何将它们合并成一个新的排序链表呢?这个问题看似简单,但其中却隐藏着不少细节和技巧。

递归比较法

最常用的方法之一就是递归比较法。我们可以将两个链表的头结点进行比较,较小的那个结点就作为新链表的头结点。然后,将较小的结点的下一个结点与较大的结点的头结点进行比较,以此类推,直到其中一个链表的结点全部被比较完。

def merge_two_sorted_lists(l1, l2):
    """
    合并两个已经排序的链表

    Args:
        l1 (ListNode): 第一个链表
        l2 (ListNode): 第二个链表

    Returns:
        ListNode: 合并后的链表
    """

    # 如果其中一个链表为空,则直接返回另一个链表
    if not l1:
        return l2
    if not l2:
        return l1

    # 比较两个链表的头结点
    if l1.val < l2.val:
        # 将较小的结点作为新链表的头结点
        new_head = l1
        # 将较小的结点的下一个结点与较大的结点的头结点进行比较
        new_head.next = merge_two_sorted_lists(l1.next, l2)
    else:
        # 将较小的结点作为新链表的头结点
        new_head = l2
        # 将较小的结点的下一个结点与较大的结点的头结点进行比较
        new_head.next = merge_two_sorted_lists(l1, l2.next)

    # 返回合并后的链表
    return new_head

哨兵结点法

另一种方法是使用哨兵结点法。我们可以创建一个新的链表,并将它的头结点指向一个哨兵结点。然后,将两个链表的头结点同时指向哨兵结点的下一个结点。接下来,我们将两个链表中的结点依次比较,并将较小的结点添加到新链表中。最后,我们将哨兵结点的下一个结点指向新链表的头结点,并返回哨兵结点。

def merge_two_sorted_lists(l1, l2):
    """
    合并两个已经排序的链表

    Args:
        l1 (ListNode): 第一个链表
        l2 (ListNode): 第二个链表

    Returns:
        ListNode: 合并后的链表
    """

    # 创建一个新的链表
    new_head = ListNode(0)
    # 将新链表的头结点指向哨兵结点
    new_head.next = ListNode(0)

    # 将两个链表的头结点同时指向哨兵结点的下一个结点
    l1_current = l1
    l2_current = l2

    # 将两个链表中的结点依次比较
    while l1_current and l2_current:
        # 如果第一个链表的结点较小
        if l1_current.val < l2_current.val:
            # 将较小的结点添加到新链表中
            new_head.next.next = l1_current
            # 将第一个链表的结点移动到下一个结点
            l1_current = l1_current.next
        # 如果第二个链表的结点较小
        else:
            # 将较小的结点添加到新链表中
            new_head.next.next = l2_current
            # 将第二个链表的结点移动到下一个结点
            l2_current = l2_current.next

    # 如果第一个链表还有剩下的结点
    while l1_current:
        # 将剩下的结点添加到新链表中
        new_head.next.next = l1_current
        # 将第一个链表的结点移动到下一个结点
        l1_current = l1_current.next

    # 如果第二个链表还有剩下的结点
    while l2_current:
        # 将剩下的结点添加到新链表中
        new_head.next.next = l2_current
        # 将第二个链表的结点移动到下一个结点
        l2_current = l2_current.next

    # 将哨兵结点的下一个结点指向新链表的头结点
    new_head.next = new_head.next.next

    # 返回新链表
    return new_head

迭代法

迭代法是另一种合并两个排序链表的方法。我们可以使用两个指针,分别指向两个链表的头结点。然后,我们将两个指针同时移动,并将较小的结点添加到新链表中。最后,我们将新链表的尾结点指向较大链表的剩余结点,并返回新链表的头结点。

def merge_two_sorted_lists(l1, l2):
    """
    合并两个已经排序的链表

    Args:
        l1 (ListNode): 第一个链表
        l2 (ListNode): 第二个链表

    Returns:
        ListNode: 合并后的链表
    """

    # 创建一个新的链表
    new_head = ListNode(0)
    # 将新链表的尾结点指向哨兵结点
    new_tail = new_head

    # 将两个指针同时指向两个链表的头结点
    l1_current = l1
    l2_current = l2

    # 将两个链表中的结点依次比较
    while l1_current and l2_current:
        # 如果第一个链表的结点较小
        if l1_current.val < l2_current.val:
            # 将较小的结点添加到新链表中
            new_tail.next = l1_current
            # 将新链表的尾结点移动到下一个结点
            new_tail = new_tail.next
            # 将第一个链表的结点移动到下一个结点
            l1_current = l1_current.next
        # 如果第二个链表的结点较小
        else:
            # 将较小的结点添加到新链表中
            new_tail.next = l2_current
            # 将新链表的尾结点移动到下一个结点
            new_tail = new_tail.next
            # 将第二个链表的结点移动到下一个结点
            l2_current = l2_current.next

    # 如果第一个链表还有剩下的结点
    while l1_current:
        # 将剩下的结点添加到新链表中
        new_tail.next = l1_current
        # 将新链表的尾结点移动到下一个结点
        new_tail = new_tail.next
        # 将第一个链表的结点移动到下一个结点
        l1_current = l1_current.next

    # 如果第二个链表还有剩下的结点
    while l2_current:
        # 将剩下的结点添加到新链表中
        new_tail.next = l2_current
        # 将新链表的尾结点移动到下一个结点
        new_tail = new_tail.next
        # 将第二个链表的结点移动到下一个结点
        l2_current = l2_current.next

    # 返回新链表的头结点
    return new_head.next

总结

以上就是合并两个已经排序的链表的三种常见方法。每种方法都有各自的优缺点,我们可以根据具体情况选择合适的方法。

除了以上三种方法之外,还有其他一些方法可以合并两个已经排序的链表,例如使用堆排序、归并排序等。这些方法更加复杂,但效率更高。