返回

剑指Offer之面试题25:有序链表合并,链上试炼!

后端

大家好,我是热爱算法与数据结构的「螺旋君」,今天,我将带着大家一起探索剑指Offer之面试题25:有序链表合并。这是一道經典的面试题,也是数据结构与算法课程的常见考点。

题目背景:

假设我们有两个有序链表,其中一个链表包含[1, 3, 5],另一个链表包含[2, 4, 6]。现在,我们需要將这两个有序链表合并,最终输出一个包含所有元素的有序链表,即[1, 2, 3, 4, 5, 6]。

解題思路:

解题思路之一是采用递归的方法:

  1. 首先,比较两个链表的表头元素,较小的元素作为新链表的表头。
  2. 然后,将较小的元素从其链表中删除,并将剩下的链表作为递归调用时的参数。
  3. 重复步骤1和步骤2,直到两个链表都为空。

这种方法的优势是简洁明了,但在链表很长时,递归调用可能会导致堆栈溢出。因此,我们还可以考虑使用迭代的方法:

  1. 设置一个哨兵结点作为新链表的表头。
  2. 比较两个链表的表头元素,较小的元素作为新链表的表尾,较大的元素从其链表中删除。
  3. 将新链表的表尾指向较小的元素,并将较小的元素从其链表中删除。
  4. 重复步骤2和步骤3,直到两个链表都为空。

这种方法的优势是避免了递归调用,但需要额外创建一个哨兵结点。

代码实现:

def merge_two_sorted_lists(l1, l2):
    """
    :param l1: 有序链表1
    :param l2: 有序链表2
    :return: 合并后的有序链表
    """
    # 设置哨兵结点
    dummy = ListNode(0)
    # 当前结点指针
    curr = dummy

    # 循环比较两个链表的表头元素
    while l1 and l2:
        # 选择较小的元素作为新链表的表尾
        if l1.val < l2.val:
            curr.next = l1
            l1 = l1.next
        else:
            curr.next = l2
            l2 = l2.next
        # 将当前结点指针移到下一个结点
        curr = curr.next

    # 将剩余的链表追加到新链表的尾部
    if l1:
        curr.next = l1
    if l2:
        curr.next = l2

    # 返回哨兵结点的下一个结点,即新链表的表头
    return dummy.next

复杂度分析:

  • 时间复杂度:O(n + m),其中n和m分别为两个链表的长度。
  • 空间复杂度:O(1),因为我们没有使用额外的空间来存储合并后的链表。

总结:

剑指Offer之面试题25:有序链表合并是一道经典的面试题,考察了算法和数据结构的基本功。通过这道题,我们可以更好地理解链表的基本操作,以及如何将两个有序链表合并成一个有序的新链表。