《剑指 Offer》学习记录:题 25:合并两个排序的链表

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

题干#

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

测试样例#

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

Copy Highlighter-hljs
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 个链表本身都是有序的,因此可以直接将剩余结点加入新链表。

题解代码#

Copy Highlighter-hljs
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。不断向上回溯,最后第一个访问的结点将会指向归并后的链表结点。

题解代码#

Copy Highlighter-hljs
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版)》,何海涛 著,电子工业出版社
【合并两个排序的链表】:迭代,递归

posted @   乌漆WhiteMoon  阅读(95)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示
CONTENTS