返回
《剑指offer》—— 合并两个排序的链表,轻松掌握
前端
2023-09-18 17:24:10
在编程的世界里,数据结构和算法是程序员的基本功。无论是构建应用程序还是解决复杂问题,都离不开数据结构和算法的支持。今天,我们就来聊聊如何合并两个已经排序的链表。
两个已经排序的链表,如何将它们合并成一个新的排序链表呢?这个问题看似简单,但其中却隐藏着不少细节和技巧。
递归比较法
最常用的方法之一就是递归比较法。我们可以将两个链表的头结点进行比较,较小的那个结点就作为新链表的头结点。然后,将较小的结点的下一个结点与较大的结点的头结点进行比较,以此类推,直到其中一个链表的结点全部被比较完。
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
总结
以上就是合并两个已经排序的链表的三种常见方法。每种方法都有各自的优缺点,我们可以根据具体情况选择合适的方法。
除了以上三种方法之外,还有其他一些方法可以合并两个已经排序的链表,例如使用堆排序、归并排序等。这些方法更加复杂,但效率更高。