返回
揭秘合并两个有序链表的妙技:手撕版算法详解!
后端
2023-09-23 02:57:27
众所周知,链表是一种常见的数据结构,在实际应用中随处可见。合并两个有序链表是LeetCode和Offer等经典面试题中常见的题目之一。该题目不仅考察了算法基础,还考验了代码实现能力。本文将为你揭开合并两个有序链表的奥秘,手把手带你写出简洁、高效的代码。
剖析算法:直击核心思想
合并两个有序链表的算法主要有两种,分别是递归算法和迭代算法。
1. 递归算法
def mergeTwoLists(l1, l2):
if not l1 or not l2:
return l1 or l2
if l1.val < l2.val:
l1.next = mergeTwoLists(l1.next, l2)
return l1
else:
l2.next = mergeTwoLists(l1, l2.next)
return l2
2. 迭代算法
def mergeTwoLists(l1, l2):
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
curr.next = l1 or l2
return dummy.next
手撕代码:从零开始构建
接下来,我们将从零开始手撕代码,一步步实现合并两个有序链表的功能。
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def mergeTwoLists(l1, l2):
# 创建一个虚拟头结点,用于简化代码
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
# 将剩余的节点添加到合并后的链表中
curr.next = l1 or l2
# 返回合并后的链表的头结点
return dummy.next
# 测试用例
l1 = ListNode(1)
l1.next = ListNode(2)
l1.next.next = ListNode(4)
l2 = ListNode(1)
l2.next = ListNode(3)
l2.next.next = ListNode(4)
# 合并两个链表
merged_list = mergeTwoLists(l1, l2)
# 打印合并后的链表
while merged_list:
print(merged_list.val)
merged_list = merged_list.next
性能优化:追求极致效率
在实际应用中,链表的长度可能非常大,因此需要考虑算法的性能优化。
1. 空间优化
def mergeTwoLists(l1, l2):
if not l1 or not l2:
return l1 or l2
if l1.val < l2.val:
l1.next = mergeTwoLists(l1.next, l2)
return l1
else:
l2.next = mergeTwoLists(l1, l2.next)
return l2
在这个优化版本中,我们不再使用额外的空间来创建虚拟头结点,而是直接返回两个链表中较小的节点作为合并后的链表的头结点。这种优化可以减少空间复杂度,但会使代码的可读性略微降低。
2. 哨兵节点优化
def mergeTwoLists(l1, l2):
# 创建一个哨兵节点,用于简化代码
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
# 将剩余的节点添加到合并后的链表中
curr.next = l1 or l2
# 返回合并后的链表的头结点的下一个节点,因为哨兵节点不包含数据
return dummy.next.next
这个优化版本在哨兵节点的基础上进行了进一步优化,直接返回哨兵节点的下一个节点作为合并后的链表的头结点,从而避免了额外的空间开销。这种优化可以同时提高空间复杂度和时间复杂度。
结语:从算法精粹到面试制胜
掌握合并两个有序链表的算法,不仅可以帮助你解决实际编程问题,还能为你的面试锦上添花。无论是递归算法还是迭代算法,理解其本质思想和实现细节,方能应对面试官的各种刁钻提问。希望本文能为你提供清晰的思路和有益的启发,助力你在面试中脱颖而出,迈向职业生涯的新高度。