【附源码】Python :链表合成(升序)

系列文章目录

Python 算法学习:链表合成(升序)



一、算法需求

给定两个升序排列的链表,采用多种方法,合并它们为一个新的升序链表。


二、方法+源码

方法1:迭代

  • 这段代码首先创建了两个链表,然后调用merge_two_lists函数进行合并,并打印合并后的链表。输出应该是1 -> 1 -> 2 -> 3 -> 4 -> 4 -> None,表示合并后的链表是升序的。

代码如下:

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

def merge_two_lists(l1, l2):
    # 创建一个虚拟头节点,便于处理链表的合并
    dummy = ListNode(0)
    current = dummy

    while l1 and l2:
        if l1.val < l2.val:
            current.next = l1
            l1 = l1.next
        else:
            current.next = l2
            l2 = l2.next
        current = current.next

    # 处理剩余节点
    current.next = l1 or l2
    return dummy.next


# 创建链表 1 -> 2 -> 4
list1 = ListNode(1, ListNode(2, ListNode(4)))

# 创建链表 1 -> 3 -> 4
list2 = ListNode(1, ListNode(3, ListNode(4)))

# 合并链表
merged_list = merge_two_lists(list1, list2)

# 打印合并后的链表
current = merged_list
while current:
    print(current.val, end=" -> ")
    current = current.next
print("None")

方法2:递归

  • 这种方法的优点是代码简洁,直观地表达了合并链表的逻辑。然而,对于非常长的链表,递归可能会导致栈溢出。在实际应用中,迭代方法通常更受青睐,因为它避免了递归调用的开销。

  • 在这个递归方法中:
    首先检查l1或l2是否为空,如果为空,则直接返回另一个非空链表。
    然后比较l1和l2当前节点的值,较小的那个节点会成为合并后链表的当前节点。
    递归地调用merge_two_lists_recursive函数来合并剩下的链表部分,并将其链接到当前节点的next指针上。
    最终返回合并后的链表的头节点。

代码如下:

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


def merge_two_lists_recursive(l1, l2):
    # 如果任意一个链表为空,直接返回另一个链表
    if not l1:
        return l2
    if not l2:
        return l1

    # 比较当前节点的值,递归合并较小的那个
    if l1.val < l2.val:
        l1.next = merge_two_lists_recursive(l1.next, l2)
        return l1
    else:
        l2.next = merge_two_lists_recursive(l1, l2.next)
        return l2


# 测试递归方法
# 创建链表 1 -> 2 -> 4
list1 = ListNode(1, ListNode(2, ListNode(4)))

# 创建链表 1 -> 3 -> 4
list2 = ListNode(1, ListNode(3, ListNode(4)))

# 合并链表
merged_list = merge_two_lists_recursive(list1, list2)

# 打印合并后的链表
current = merged_list
while current:
    print(current.val, end=" -> ")
    current = current.next
print("None")

方法3:函数sorted

  • 这种方法利用了Python的排序功能,将两个链表的元素合并到一个列表中,然后对列表进行排序,最后将排序后的列表转换回链表。这种方法简单且易于理解,但可能在性能上不如迭代和递归方法,因为它涉及到额外的列表操作和排序时间。

  • 在这个实现中:
    listify 函数用于将链表转换为列表,通过遍历链表并收集每个节点的值。
    to_linked_list 函数用于将列表转换回链表,通过创建新的链表节点并链接它们。
    merge_two_lists_sorted 函数首先将两个链表转换为列表,然后将这两个列表合并并使用sorted函数进行排序,最后将排序后的列表转换回链表。

  • 这种方法的优点是代码简洁,易于实现,特别是对于初学者来说。然而,它在处理非常大的数据集时可能不够高效,因为列表操作和排序过程可能会消耗较多时间和内存。对于需要高性能的场景,迭代或递归方法通常是更好的选择。

代码如下:

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

def merge_two_lists_sorted(l1, l2):
    # 将链表转换为列表
    def listify(head):
        result = []
        while head:
            result.append(head.val)
            head = head.next
        return result
    
    # 将列表转换回链表
    def to_linked_list(lst):
        dummy = ListNode(0)
        current = dummy
        for val in lst:
            current.next = ListNode(val)
            current = current.next
        return dummy.next
    
    # 合并两个链表的元素并排序
    merged_list = sorted(listify(l1) + listify(l2))
    return to_linked_list(merged_list)

# 测试排序方法
# 创建链表 1 -> 2 -> 4
list1 = ListNode(1, ListNode(2, ListNode(4)))

# 创建链表 1 -> 3 -> 4
list2 = ListNode(1, ListNode(3, ListNode(4)))

# 合并链表
merged_list = merge_two_lists_sorted(list1, list2)

# 打印合并后的链表
current = merged_list
while current:
    print(current.val, end=" -> ")
    current = current.next
print("None")

方法4:双指针

  • 这种方法与迭代方法类似,但是它使用两个指针分别遍历两个链表,并将节点插入到一个新的链表中,而不是直接链接到当前节点。这种方法可以减少链表节点的重新分配,因为它避免了在迭代过程中不断更新节点的next指针。

  • 在这个实现中:
    dummy 是一个虚拟头节点,用于简化链表的构建过程。
    current 是一个指针,用于构建新的链表。
    使用两个while循环分别遍历两个链表,每次比较当前节点的值,并将较小值的节点链接到新链表的末尾。
    当其中一个链表遍历完成后,将另一个链表的剩余部分直接链接到新链表的末尾。
    最后返回dummy.next,即合并后的链表的头节点。

  • 这种方法的优点是它直接在原链表上进行操作,减少了内存的使用,并且避免了递归调用的开销。它适用于需要高效合并链表的场景。

代码如下:

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

def merge_two_lists_pointer(l1, l2):
    dummy = ListNode(0)  # 创建一个虚拟头节点
    current = dummy  # 使用current指针来构建新链表

    while l1 and l2:
        if l1.val < l2.val:
            current.next = l1
            l1 = l1.next
        else:
            current.next = l2
            l2 = l2.next
        current = current.next

    # 处理剩余节点
    current.next = l1 if l1 is not None else l2

    return dummy.next  # 返回合并后的链表头节点

# 测试双指针方法
# 创建链表 1 -> 2 -> 4
list1 = ListNode(1, ListNode(2, ListNode(4)))

# 创建链表 1 -> 3 -> 4
list2 = ListNode(1, ListNode(3, ListNode(4)))

# 合并链表
merged_list = merge_two_lists_pointer(list1, list2)

# 打印合并后的链表
current = merged_list
while current:
    print(current.val, end=" -> ")
    current = current.next
print("None")

方法5:heapq模块

  • 该模块提供了一个基于堆的优先队列实现。我们可以将链表的元素添加到优先队列中,然后逐个取出最小的元素来构建新的链表。这种方法利用了堆的性质,可以高效地获取最小元素。

  • 在这个实现中:
    使用heapq模块的heappush和heappop函数来添加和移除元素。
    每个元素是一个包含值、来源链表索引和节点本身的元组,这样可以在取出最小元素时知道下一步应该从哪个链表中取值。
    使用一个虚拟头节点dummy和current指针来构建新的链表。
    循环直到优先队列为空,每次循环中取出最小元素并将其链接到新链表中,然后检查并添加下一个节点到优先队列。

  • 这种方法的优点是它可以高效地处理两个链表的合并,特别是当链表很长且元素数量不均衡时。然而,它需要额外的空间来存储优先队列,并且对于链表操作来说可能不如直接的链表操作直观。

代码如下:

import heapq
from collections import namedtuple

# 定义一个包含值和来源链表索引的元组
ListNode = namedtuple('ListNode', ['val', 'node'])

def merge_two_lists_heap(l1, l2):
    # 创建一个优先队列
    heap = []
    
    # 将两个链表的头节点添加到优先队列中
    if l1:
        heapq.heappush(heap, (l1.val, 0, l1))
    if l2:
        heapq.heappush(heap, (l2.val, 1, l2))
    
    dummy = ListNode(0)  # 创建一个虚拟头节点
    current = dummy  # 使用current指针来构建新链表
    
    # 从优先队列中逐个取出最小的元素
    while heap:
        val, list_idx, node = heapq.heappop(heap)
        current.next = node
        current = current.next
        # 如果当前节点的下一个节点不为空,则将其添加到优先队列中
        if node.next:
            heapq.heappush(heap, (node.next.val, list_idx, node.next))
    
    return dummy.next  # 返回合并后的链表头节点

# 测试heapq方法
# 创建链表 1 -> 2 -> 4
list1 = ListNode(1, None)
list1.node = ListNode(2, None)
list1.node.node = ListNode(4, None)

# 创建链表 1 -> 3 -> 4
list2 = ListNode(1, None)
list2.node = ListNode(3, None)
list2.node = ListNode(4, None)

# 合并链表
merged_list = merge_two_lists_heap(list1, list2)

# 打印合并后的链表
current = merged_list
while current:
    print(current.val, end=" -> ")
    current = current.next
print("None")

总结

总而言之,每种方法都有其适用场景和优缺点。迭代和递归是最常用的方法,而双指针技术则提供了一种减少内存分配的方式。使用heapq模块的方法适合处理复杂的数据结构合并问题,而Python函数则提供了一种通用的解决方案。

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱吃饭团的饭桶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值