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

news/2025/2/21 10:06:26/文章来源:https://www.cnblogs.com/wgjava/p/18724556

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

  • 反转链表

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

  • 合并链表

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

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

反转链表

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

迭代方法

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

# 定义链表节点类
class ListNode:def __init__(self, val=0, next=None):self.val = valself.next = nextdef reverseList(head):# 初始化前一个节点为 Noneprev = None# 当前节点指向头节点curr = headwhile 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 = dummyfor val in lst:current.next = ListNode(val)current = current.nextreturn dummy.next# 辅助函数:将链表转换为列表
def linked_list_to_list(head):result = []current = headwhile current:result.append(current.val)current = current.nextreturn 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 = valself.next = nextdef reverseList(head):# 如果链表为空或只有一个节点,直接返回头节点if not head or not head.next:return head# 递归地反转当前节点之后的链表new_head = reverseList(head.next)# 将当前节点的下一个节点的指针指向当前节点head.next.next = head# 将当前节点的指针置为 Nonehead.next = Nonereturn new_head# 辅助函数:将列表转换为链表
def list_to_linked_list(lst):dummy = ListNode(0)current = dummyfor val in lst:current.next = ListNode(val)current = current.nextreturn dummy.next# 辅助函数:将链表转换为列表
def linked_list_to_list(head):result = []current = headwhile current:result.append(current.val)current = current.nextreturn 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 = valself.next = nextdef mergeTwoLists(l1, l2):# 创建一个虚拟头节点dummy = ListNode(0)# 当前节点指针,初始指向虚拟头节点current = dummywhile l1 and l2:if l1.val <= l2.val:# 如果 l1 的值较小,将 l1 节点添加到结果链表current.next = l1l1 = l1.nextelse:# 如果 l2 的值较小,将 l2 节点添加到结果链表current.next = l2l2 = l2.next# 移动当前节点指针current = current.next# 将剩余的链表连接到结果链表末尾if l1:current.next = l1if l2:current.next = l2# 返回合并后链表的头节点(虚拟头节点的下一个节点)return dummy.next# 辅助函数:将列表转换为链表
def list_to_linked_list(lst):dummy = ListNode(0)current = dummyfor val in lst:current.next = ListNode(val)current = current.nextreturn dummy.next# 辅助函数:将链表转换为列表
def linked_list_to_list(head):result = []current = headwhile current:result.append(current.val)current = current.nextreturn 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 = valself.next = nextdef mergeTwoLists(l1, l2):dummy = ListNode(0)current = dummywhile l1 and l2:if l1.val <= l2.val:current.next = l1l1 = l1.nextelse:current.next = l2l2 = l2.nextcurrent = current.nextif l1:current.next = l1if l2:current.next = l2return dummy.nextdef mergeKLists(lists):if not lists:return Nonewhile 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 Nonemerged = mergeTwoLists(l1, l2)merged_lists.append(merged)lists = merged_listsreturn lists[0]# 辅助函数:将列表转换为链表
def list_to_linked_list(lst):dummy = ListNode(0)current = dummyfor val in lst:current.next = ListNode(val)current = current.nextreturn dummy.next# 辅助函数:将链表转换为列表
def linked_list_to_list(head):result = []current = headwhile current:result.append(current.val)current = current.nextreturn 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 = valself.next = nextdef mergeTwoLists(l1, l2):# 创建虚拟头节点dummy = ListNode(0)current = dummy# 处理链表为空的情况if not l1:return l2if not l2:return l1while l1 and l2:if l1.val <= l2.val:current.next = l1l1 = l1.nextelse:current.next = l2l2 = l2.nextcurrent = current.nextif l1:current.next = l1if l2:current.next = l2return dummy.next# 辅助函数:将列表转换为链表
def list_to_linked_list(lst):dummy = ListNode(0)current = dummyfor val in lst:current.next = ListNode(val)current = current.nextreturn dummy.next# 辅助函数:将链表转换为列表
def linked_list_to_list(head):result = []current = headwhile current:result.append(current.val)current = current.nextreturn 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 = valself.next = nextdef mergeTwoLists(l1, l2):# 处理链表为空的情况if not l1:return l2if not l2:return l1if l1.val <= l2.val:l1.next = mergeTwoLists(l1.next, l2)return l1else:l2.next = mergeTwoLists(l1, l2.next)return l2# 辅助函数省略,同上面代码

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

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

最后

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/886758.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

最新更新!扣子(Coze)接入地表最强DeepSeek-R1大模型,超全攻略,手把手教学,完全免费教程

‍ 最新消息,国产地表最强大模型可以接入Coze平台了,今天斜杠君为大家带来了最细接入攻略,大家快学起来吧~ 备注:需要登录专业版火上引擎接入,开通专业版的同学需要开通一下。 接下来,话不多说,斜杠君用最简单的方式教给大家。 大家可以关注收藏,以免之后找不到,而且也…

1.如何在python中安装playwright

1.如何在python中安装playwright 打开pycharm,进入终端,输入如下的2个命令行代码即可自动完成playwright的安装 pip install playwright ——》在python中安装playwright第三方模块 playwright install ——》安装playwright所需的工具插件和所支持的浏览器 看到这里,是否想…

最新扣子(Coze)案例教程:DeepSeek 图像生成,用扣子应用打造超萌表情包生成器,手把手教学,完全免费教程

上一篇文章和大家分享了如何把DeepSeek-R1接入到扣子智能体中进行使用,这篇教程让我们来应用一下DeepSeek,使用DeepSeek结合工作流中的图像生成节点,打造一个表情包生成器的应用。 应用作用:输入一个人物或动物主题,生成一组表情包。 首先我们来看一下生成后的效果: 图像…

我悟了!原来本地图片预览还能这样搞

在网页开发中,经常会遇到需要让用户上传图片并在上传前进行预览的需求。这样做的好处显而易见:用户可以立即看到自己选择的图片是否正确,避免了不必要的上传和服务器资源浪费,提升了用户体验。Hey, 我是 Immerse 本文首发于 【沉浸式趣谈】,我的个人博客 https://yaolifen…

octave画高通滤波、超前,滞后补偿器的幅频响应图

octave代码非常简单:pkg load control s=tf(s); k=0.5; sysG1=k*(0.005*s)/(0.005*s+1); sysG2=k*(0.8*s+1)/(0.1*s+1); sysG3=k*(s+1)/(5*s+1); figure bode(sysG1) figure bode(sysG2) figure bode(sysG3)也可以借助循环,看如下代码:1 pkg load control2 s=tf(s);3 k=0.5;…

frame切换/窗口切换

frame切换/窗口切换 切换到frame点击这里,边看视频讲解,边学习以下内容 请大家点击这里,打开这个链接 如果我们要 选择 下图方框中 所有的 蔬菜,使用css选择,怎么写表达式? 当然,要先查看到它们的html元素特征大家可能会照旧写出如下代码:from selenium import webdriv…

选择元素的基本方法

选择元素的基本方法点击这里,边看视频讲解,边学习以下内容对于百度搜索页面,如果我们想自动化输入 白月黑羽 ,怎么做呢? 这就是在网页中,操控界面元素。 web界面自动化,要操控元素,首先需要 选择 界面元素 ,或者说 定位 界面元素 就是 先告诉浏览器,你要操作 哪个 …

操控元素的基本方法

操控元素的基本方法 点击这里,边看视频讲解,边学习以下内容选择到元素之后,我们的代码会返回元素对应的 WebElement对象,通过这个对象,我们就可以 操控 元素了。 操控元素通常包括 点击元素在元素中输入字符串,通常是对输入框这样的元素获取元素包含的信息,比如文本内容…

CClink IEF Basic设备数据 转EthernetIP项目案例

VFBOX协议转换网关支持PLC,modbus,EthernetIP,Profinet,CCLink,EtherCAT,IEC61850,IEC104,bacnet,DLT645,HJ212,opc ua,opc da,DNP3。目录 1 案例说明 1 2 VFBOX网关工作原理 1 3 准备工作 2 4 网关采集CCLINK IEF BASIC数据 2 5 使用ETHERNETIP转发数据 5 6 案例…

SciTech-EECS-BigDataAIML-NN(神经网络): Forward NN(前向传播算法)

SciTech-EECS-BigDataAIML-NN(神经网络): Forward NN(前向传播算法)

微信小程序-实现微信授权自动登录功能

显示效果 授权前界面授权弹框界面 采用了wx.getUserProfile API申请用户信息,若基础库版本过高,例如3.7.7,则不会显示弹窗,默认为允许状态。若基础库版本降低,例如2.25.4,则会显示弹窗,如下图所示授权后界面前端部分 基础封装 新建utils文件夹,在该文件夹下新建login.j…

为DeepSeek添加本地知识库

为什么要尝试给DeepSeek添加本地知识库呢?作为一个程序员,以前也用过很多AI产品,直到春节DeepSeek爆火,成功在自己的电脑上把AI模型跑起来的时候才真正感受到AI已近在咫尺。未来很多应用和项目都离不开这个工具。或许在我们OA系统中会出现一个AI小助手,我们会问它差旅费报…