zoukankan      html  css  js  c++  java
  • 《剑指 Offer》学习记录:题 25:合并两个排序的链表

    题 25:合并两个排序的链表

    题干

    输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。——《剑指 Offer》P145

    测试样例

    链表的数据结构定义如下(Python):

    class ListNode:
        def __init__(self, x):
            self.val = x
            self.next = None
    

    若传入如下 2 个有序的链表:

    则 2 个链表合并后,得到的链表也是要有序的:

    二路归并

    方法思路

    有序表的二路归并,思路还是比较清晰的。由于 2 个链表本身是有序的,因此只需要依次从 2 张表中选择适合的元素,用尾插法插入新表即可。例如对于如下结构,指针 ptr1 指向第一个链表的首元结点,指针 ptr2 指向第一个链表的首元结点,开辟一个头结点 head 作为新链表。

    由于新链表为升序,因此考查 ptr1 和 ptr2 指向的结点,ptr1 指向的结点加入 head。head 插入新结点之后,需要将 ptr1 向后移动。

    ptr1 和 ptr2 指向的结点,ptr2 指向的结点的 val 较小所以加入 head。head 插入新结点之后,需要将 ptr2 向后移动。

    重复上述操作,依次将结点都加入到新链表。

    由于此时 ptr1 已经指向表尾,也就是说链表 1 的结点都加入新链表了。对于链表 2 剩余的元素,由于 2 个链表本身都是有序的,因此可以直接将剩余结点加入新链表。

    题解代码

    class Solution:
        def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
            ptr = head = ListNode(0)    #新链表的头结点
            while l1 and l2:    #考查 2 张链表的接点,依次加入新链表
                if l1.val <= l2.val:
                    ptr.next = l1    
                    l1 = l1.next
                else:
                    ptr.next = l2
                    l2 = l2.next
                ptr = ptr.next
    
            if l1 != None:
                ptr.next = l1    #链表 1 的剩余结点加入新链表
            else:
                ptr.next = l2    #链表 2 的剩余结点加入新链表
            
            return head.next
    

    时空复杂度

    设链表 1 的表长为 m,链表 2 的表长为 n。由于需要遍历 2 张链表,因此时间复杂度为 O(m + n)。
    将 2 个有序链表归并为一张链表不需要额外空间,因此空间复杂度为 O(1)。

    递归法

    方法思路

    递归法相对比较巧妙,递归层次在的是对于新链表的结点,递归函数的返回值是递归层次所在结点的后继。递归的出口是还未全部加入新链表的剩余结点,例如如图所示情况,结点 6 是递归访问链表 1 后,链表 2 的剩余结点,这个是递归的出口开始回溯。返回的上一个递归层次是结点 5,让结点 5 的后继为递归函数的返回值。

    再次回溯,结点 5 作为递归函数的返回值,当前递归层次所在的结点是 4。不断向上回溯,最后第一个访问的结点将会指向归并后的链表结点。

    题解代码

    class Solution:
        def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
            if l1 == None:
                return l2    #链表 1 访问完毕,返回剩余链表 2 剩余结点
            elif l2 == None:
                return l1    #链表 2 访问完毕,返回剩余链表 1 剩余结点
            
            if l1.val <= l2.val:
                l1.next = self.mergeTwoLists(l1.next, l2)
                return l1
            else:
                l2.next = self.mergeTwoLists(l1, l2.next)
                return l2
    

    时空复杂度

    设链表 1 的表长为 m,链表 2 的表长为 n。由于需要访问 2 张链表的所有结点,因此时间复杂度为 O(m + n)。
    由于递归需要占用额外的空间保存状态,因此空间复杂度为 O(n)。

    参考资料

    《剑指 Offer(第2版)》,何海涛 著,电子工业出版社
    【合并两个排序的链表】:迭代,递归

  • 相关阅读:
    杭电2081
    杭电2083
    杭电2084
    3/5/2014 cfb 小心
    116
    uva10003
    10815
    127
    674
    uva 13598
  • 原文地址:https://www.cnblogs.com/linfangnan/p/14673751.html
Copyright © 2011-2022 走看看