数据结构与算法 —— 链表linked list(01)
链表(维基百科)
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。在计算机科学中,链表作为一种基础的数据结构可以用来生成其它类型的数据结构。链表通常由一连串节点组成,每个节点包含任意的实例数据(data fields)和一或两个用来指向上一个/或下一个节点的位置的链接("links")。链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的访问往往要在不同的排列顺序中转换。而链表是一种自我指示数据类型,因为它包含指向另一个相同类型的数据的指针(链接)。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表,双向链表以及循环链表。单向链表(又名单链表、线性链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过从头部开始,依序往下读取.
定义
我们来看一下单向链表的定义实现:
1 2 3 4 | class ListNode( object ): def __init__( self , value): self .value = value self . next = None |
一个节点有value的属性定义该节点的值,还有一个next的指针,指向下一个节点,类似于1>2>4>5>6>3这样。
操作
单向链表的操作主要有:添加一个节点,删除一个节点,遍历一条链表,具体的实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | def add(pre, new_node): """ pre节点后面插入一个新的节点 :param pre: pre节点 :param new_node: 新插入的节点 :return: """ new_code. next = pre. next pre. next = new_node def delete(pre): """ pre节点的后面删除一个节点 :return: """ if pre. next : pre. next = pre. next . next def traverse(head): while head: print (head.value) head = head. next |
在pre节点后面插入一个节点:只需要把新节点的指针指向pre节点指针指向的节点,把pre节点的指针指向新节点。
在pre节点后面删除一个节点:只需要把pre节点的指针指向pre节点的指针的指针节点(要注意pre节点的指针不为None)
题目
我们来看一个关于链表的一个算法题,来自于leetcode:
删除链表中等于给定值 val 的所有元素。
示例
给定: 1 --> 2 --> 6 --> 3 --> 4 --> 5 --> 6, val = 6
返回: 1 --> 2 --> 3 --> 4 --> 5
分析:一般来讲,我们首先会考虑到遍历一下这个链表,移除掉value值等于给定val的节点就好。但是这里要返回一个新的链表,我们知道,对于单向链表,我们只能知道当前元素的后置节点,而不知道当前元素的前置节点。所以我们要构建一个前置节点。
看代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | def removeElements(head, val): """ :param head: listNode :param val: int :return: listNode """ dump = ListNode( - 1 ) dump. next = head cur = head pre = dump while cur: if cur.value = = val: pre. next = cur. next else : pre = pre. next cur = cur. next return dump. next |
我们构建一个dump节点作为第一个节点,cur节点为当前循环的节点,pre节点为cur节点的前置节点。
当前节点的value为给定val的时候,把当前cur的前置节点pre指针指向cur的后置节点,也就是cur.next。然后接着遍历这个链表。
由于第一个节点dump是我们自己构造的(我用的值是-1,你可以用其他不为val的值,一般使用-1),它的next是给出的head,我们只是对head做了调整。所以最后新的列表就是dump.next,也就是调整后的head。
学习技术交流群:226704167,愿和各位一起进步!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端