Python 实现反转、合并链表有啥用?

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
简介: 大家好,我是V哥。本文介绍Python实现反转链表和合并链表的应用场景及代码实现。反转链表适用于时间序列数据展示、回文链表判断等;合并链表则用于大规模数据排序、数据库查询结果集合并等。通过迭代和递归方法实现反转链表,以及合并两个或多个有序链表的算法,帮助开发者解决实际问题。关注V哥,了解更多实用编程技巧。先赞再看后评论,腰缠万贯财进门。

大家好,我是 V 哥。使用 Python 实现反转链表、合并链表在开发中比较常见,我们先来看看各自的应用场景。先赞再看后评论,腰缠万贯财进门

  • 反转链表

比如,在处理时间序列数据时,有时需要将历史数据按照时间从近到远的顺序展示,如果数据是以链表形式存储的,通过反转链表可以高效地实现这一需求。再比如,判断一个链表是否为回文链表(即链表正序和逆序遍历的值相同)时,可以先反转链表的后半部分,然后与前半部分进行比较。再比如,在图像处理中,有时需要对图像进行水平或垂直翻转。如果图像数据以链表形式存储(例如,链表中的每个节点代表图像的一个像素),反转链表可以实现图像的水平翻转。

  • 合并链表

比如,在大规模数据排序中,当数据量太大无法一次性加载到内存中时,可以采用多路归并排序算法。该算法将数据分成多个小块,分别排序后得到多个有序链表,然后通过合并这些有序链表得到最终的有序结果。合并链表是多路归并排序的核心操作之一。在数据库中,当执行多个查询操作并得到多个有序结果集时,需要将这些结果集合并成一个有序的结果集。如果这些结果集以链表形式存储,合并链表可以高效地完成这个任务。在多媒体处理中,有时需要将多个音视频流合并成一个流。如果每个音视频流的数据以链表形式存储,合并链表可以实现音视频流的合并。

了解完反转链表和合并链表的应用场景,是不是跟 V 哥一样,这玩意儿还真挺有用的,那接下来,V 哥就详细介绍一个反转链表和合并链表。

反转链表

先看在 Python 中实现反转链表,我们可以使用迭代和递归两种方法。下面分别给出这两种方法的详细实现。

迭代方法

迭代方法的核心思想是遍历链表,在遍历过程中改变每个节点的指针方向,使其指向前一个节点。

# 定义链表节点类
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def reverseList(head):
    # 初始化前一个节点为 None
    prev = None
    # 当前节点指向头节点
    curr = head
    while curr:
        # 保存当前节点的下一个节点
        next_node = curr.next
        # 将当前节点的指针指向前一个节点
        curr.next = prev
        # 前一个节点移动到当前节点
        prev = curr
        # 当前节点移动到下一个节点
        curr = next_node
    # 最终 prev 指向反转后的头节点
    return prev

# 辅助函数:将列表转换为链表
def list_to_linked_list(lst):
    dummy = ListNode(0)
    current = dummy
    for val in lst:
        current.next = ListNode(val)
        current = current.next
    return dummy.next

# 辅助函数:将链表转换为列表
def linked_list_to_list(head):
    result = []
    current = head
    while current:
        result.append(current.val)
        current = current.next
    return result

# 测试代码
input_list = [1, 2, 3, 4, 5]
head = list_to_linked_list(input_list)
reversed_head = reverseList(head)
output_list = linked_list_to_list(reversed_head)
print(output_list)  # 输出: [5, 4, 3, 2, 1]

递归方法

递归方法的核心思想是先递归地反转当前节点之后的链表,然后将当前节点的指针指向前一个节点。

# 定义链表节点类
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def reverseList(head):
    # 如果链表为空或只有一个节点,直接返回头节点
    if not head or not head.next:
        return head
    # 递归地反转当前节点之后的链表
    new_head = reverseList(head.next)
    # 将当前节点的下一个节点的指针指向当前节点
    head.next.next = head
    # 将当前节点的指针置为 None
    head.next = None
    return new_head

# 辅助函数:将列表转换为链表
def list_to_linked_list(lst):
    dummy = ListNode(0)
    current = dummy
    for val in lst:
        current.next = ListNode(val)
        current = current.next
    return dummy.next

# 辅助函数:将链表转换为列表
def linked_list_to_list(head):
    result = []
    current = head
    while current:
        result.append(current.val)
        current = current.next
    return result

# 测试代码
input_list = [1, 2, 3, 4, 5]
head = list_to_linked_list(input_list)
reversed_head = reverseList(head)
output_list = linked_list_to_list(reversed_head)
print(output_list)  # 输出: [5, 4, 3, 2, 1]

以上两种方法都可以实现链表的反转,迭代方法的时间复杂度是 $O(n)$,空间复杂度是 $O(1)$;递归方法的时间复杂度也是 $O(n)$,但空间复杂度是 $O(n)$,主要是递归调用栈的开销。

使用 Python 实现链表的合并

在 Python 中实现链表的合并,常见的情况有合并两个有序链表和合并多个有序链表,下面分别介绍这两种情况的实现方法。

合并两个有序链表

合并两个有序链表的思路是比较两个链表当前节点的值,将较小值的节点添加到结果链表中,然后移动相应链表的指针,直到其中一个链表遍历完,最后将另一个链表剩余的部分直接连接到结果链表的末尾。

# 定义链表节点类
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def mergeTwoLists(l1, l2):
    # 创建一个虚拟头节点
    dummy = ListNode(0)
    # 当前节点指针,初始指向虚拟头节点
    current = dummy
    while l1 and l2:
        if l1.val <= l2.val:
            # 如果 l1 的值较小,将 l1 节点添加到结果链表
            current.next = l1
            l1 = l1.next
        else:
            # 如果 l2 的值较小,将 l2 节点添加到结果链表
            current.next = l2
            l2 = l2.next
        # 移动当前节点指针
        current = current.next
    # 将剩余的链表连接到结果链表末尾
    if l1:
        current.next = l1
    if l2:
        current.next = l2
    # 返回合并后链表的头节点(虚拟头节点的下一个节点)
    return dummy.next

# 辅助函数:将列表转换为链表
def list_to_linked_list(lst):
    dummy = ListNode(0)
    current = dummy
    for val in lst:
        current.next = ListNode(val)
        current = current.next
    return dummy.next

# 辅助函数:将链表转换为列表
def linked_list_to_list(head):
    result = []
    current = head
    while current:
        result.append(current.val)
        current = current.next
    return result

# 测试代码
list1 = [1, 2, 4]
list2 = [1, 3, 4]
l1 = list_to_linked_list(list1)
l2 = list_to_linked_list(list2)
merged_head = mergeTwoLists(l1, l2)
merged_list = linked_list_to_list(merged_head)
print(merged_list)  # 输出: [1, 1, 2, 3, 4, 4]

合并多个有序链表

合并多个有序链表可以使用分治法,不断地将链表两两合并,直到最终合并成一个链表。

# 定义链表节点类
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def mergeTwoLists(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
    if l1:
        current.next = l1
    if l2:
        current.next = l2
    return dummy.next

def mergeKLists(lists):
    if not lists:
        return None
    while len(lists) > 1:
        merged_lists = []
        for i in range(0, len(lists), 2):
            l1 = lists[i]
            l2 = lists[i + 1] if i + 1 < len(lists) else None
            merged = mergeTwoLists(l1, l2)
            merged_lists.append(merged)
        lists = merged_lists
    return lists[0]

# 辅助函数:将列表转换为链表
def list_to_linked_list(lst):
    dummy = ListNode(0)
    current = dummy
    for val in lst:
        current.next = ListNode(val)
        current = current.next
    return dummy.next

# 辅助函数:将链表转换为列表
def linked_list_to_list(head):
    result = []
    current = head
    while current:
        result.append(current.val)
        current = current.next
    return result

# 测试代码
lists = [[1, 4, 5], [1, 3, 4], [2, 6]]
linked_lists = [list_to_linked_list(lst) for lst in lists]
merged_head = mergeKLists(linked_lists)
merged_list = linked_list_to_list(merged_head)
print(merged_list)  # 输出: [1, 1, 2, 3, 4, 4, 5, 6]

以上代码分别实现了合并两个有序链表和合并多个有序链表的功能,通过辅助函数可以方便地进行链表和列表之间的转换,便于测试。

合并两个链表的过程中,是否需要考虑链表为空的情况?

在合并两个链表的过程中,需要考虑链表为空的情况,下面从必要性和不同实现情况来详细分析:

必要性

考虑链表为空的情况是非常必要的,原因如下:

  • 避免程序出错:如果不处理链表为空的情况,在代码中直接访问空链表的节点属性(如 valnext),会引发 AttributeError 异常,导致程序崩溃。
  • 保证逻辑完整性:在实际应用中,链表为空是一种合理的输入情况,处理这种边界情况可以让代码更加健壮,能够适应各种输入场景。

不同实现情况的处理

合并两个有序链表

下面是考虑链表为空情况的合并两个有序链表的代码:

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

def mergeTwoLists(l1, l2):
    # 创建虚拟头节点
    dummy = ListNode(0)
    current = dummy
    # 处理链表为空的情况
    if not l1:
        return l2
    if not l2:
        return l1
    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
    if l1:
        current.next = l1
    if l2:
        current.next = l2
    return dummy.next

# 辅助函数:将列表转换为链表
def list_to_linked_list(lst):
    dummy = ListNode(0)
    current = dummy
    for val in lst:
        current.next = ListNode(val)
        current = current.next
    return dummy.next

# 辅助函数:将链表转换为列表
def linked_list_to_list(head):
    result = []
    current = head
    while current:
        result.append(current.val)
        current = current.next
    return result

# 测试链表为空的情况
list1 = []
list2 = [1, 2, 3]
l1 = list_to_linked_list(list1)
l2 = list_to_linked_list(list2)
merged_head = mergeTwoLists(l1, l2)
merged_list = linked_list_to_list(merged_head)
print(merged_list)  # 输出: [1, 2, 3]

在上述代码中,在函数开始处就对链表是否为空进行了判断,如果其中一个链表为空,直接返回另一个链表。这样可以避免后续代码在访问空链表节点时出现错误。

递归实现合并两个有序链表

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

def mergeTwoLists(l1, l2):
    # 处理链表为空的情况
    if not l1:
        return l2
    if not l2:
        return l1
    if l1.val <= l2.val:
        l1.next = mergeTwoLists(l1.next, l2)
        return l1
    else:
        l2.next = mergeTwoLists(l1, l2.next)
        return l2

# 辅助函数省略,同上面代码

在递归实现中,同样在函数开始就对链表为空的情况进行了处理,确保递归调用时不会出现访问空节点属性的错误。

所以,在合并两个链表时,考虑链表为空的情况是必不可少的,这样可以增强代码的健壮性和可靠性。

最后

反转链表和合并链表是链表操作中的基础且重要的算法,在很多实际应用场景中都有广泛的用途,就如 V 哥文章开头介绍的应用场景,如果不懂应用场景来学链表反转、合并,即使掌握了实现原理,也只是学会了招式,而不懂为什么学。关注威哥爱编程,全栈开发就你行。

相关实践学习
基于Hologres轻松玩转一站式实时仓库
本场景介绍如何利用阿里云MaxCompute、实时计算Flink和交互式分析服务Hologres开发离线、实时数据融合分析的数据大屏应用。
Linux入门到精通
本套课程是从入门开始的Linux学习课程,适合初学者阅读。由浅入深案例丰富,通俗易懂。主要涉及基础的系统操作以及工作中常用的各种服务软件的应用、部署和优化。即使是零基础的学员,只要能够坚持把所有章节都学完,也一定会受益匪浅。
相关文章
|
26天前
|
存储 Python
Python 实现单向链表,和单向链表的反转
链表是一种数据结构,每个节点存储相邻节点的位置信息。单链表中的节点仅存储下一节点的位置。通过Python实现单链表,定义`ListNode`类并关联节点可创建链表。例如,创建A-&gt;B-&gt;C的链表后,可通过反转函数`reverse`将链表反转为CBA。代码展示了如何实现和操作单链表。
Python 实现单向链表,和单向链表的反转
|
7月前
|
Python
【Leetcode刷题Python】21. 合并两个有序链表
介绍了几种不同的方法来合并多个已排序的链表,包括暴力求解、使用小顶堆以及分而治之策略。
69 2
|
2月前
|
Python
探索 Python 中链表的实现:从基础到高级
链表是一种由节点组成的基础数据结构,每个节点包含数据和指向下一个节点的引用。本文通过Python类实现单向链表,详细介绍了创建、插入、删除节点等操作,并提供示例代码帮助理解。链表在处理动态数据时具有高效性,适用于大量数据变动的场景。文章为初学者提供了全面的入门指南,助你掌握链表的核心概念与应用。
104 0
|
7月前
|
索引 Python
【Leetcode刷题Python】328. 奇偶链表
在不使用额外空间的情况下,将链表中的奇数和偶数索引节点重新排序的方法,并提供了相应的Python实现代码。
62 0
|
7月前
|
Python
【Leetcode刷题Python】25.K 个一组翻转链表
解决LeetCode "K 个一组翻转链表" 问题的三种方法:使用栈、尾插法和虚拟节点顺序法,并提供了每种方法的Python实现代码。
54 0
|
7月前
|
Python
【Leetcode刷题Python】114. 二叉树展开为链表
LeetCode上114号问题"二叉树展开为链表"的Python实现,通过先序遍历二叉树并调整节点的左右指针,将二叉树转换为先序遍历顺序的单链表。
41 3
【Leetcode刷题Python】114. 二叉树展开为链表
|
7月前
|
Python
【Leetcode刷题Python】剑指 Offer 22. 链表中倒数第k个节点
Leetcode题目"剑指 Offer 22. 链表中倒数第k个节点"的Python解决方案,使用双指针法找到并返回链表中倒数第k个节点。
77 5
|
7月前
|
Python
【Leetcode刷题Python】剑指 Offer 18. 删除链表的节点
Leetcode题目"剑指 Offer 18. 删除链表的节点"的Python解决方案,通过使用双指针法找到并删除链表中值为特定数值的节点,然后返回更新后的链表头节点。
68 4
|
7月前
|
存储 Python
【Leetcode刷题Python】23. 合并K个升序链表
合并K个升序链表的方法:使用数组排序的暴力求解法、使用小顶堆的高效方法,以及分而治之的策略,并提供了相应的Python实现代码。
41 1
|
7月前
|
Python
【Leetcode刷题Python】138. 复制带随机指针的链表
LeetCode上题目“138. 复制带随机指针的链表”的Python解决方案,包括两种方法:一种是在每个节点后复制一个新节点然后再分离出来形成新链表;另一种是构建一个字典来跟踪原始节点与其副本之间的映射关系,从而处理新链表的构建。
44 1