数据结构——线性结构(链表)
链表定义:链表是由一系列节点组成的元素结合。每个节点包含两个部分,数据域item和指向下一个节点的指针next。通过节点之间的相互连接,最终串联成一个链表。
一、单链表
1、节点定义
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Node: def __init__( self , item): self .item = item self . next = None # 模拟链表 a = Node( 1 ) b = Node( 2 ) c = Node( 3 ) a. next = b b. next = c print (a. next . next .item) # 输出:3 |
2、建立链表
(1)头插法
头插法是在头结点这边插入。
(2)尾插法
不光要知道头还需要知道尾在哪。从尾节点插入。
(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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | class Node: def __init__( self , item): self .item = item # 存放数据 self . next = None # 指针,指向下一个节点 def create_linklist_head(li): """头插法创建链表""" head = Node(li[ 0 ]) # 头节点 for element in li[ 1 :]: # 从第二个到最后一个遍历列表 node = Node(element) # 实例化为一个链表节点 node. next = head # 设置实例的next属性指向链表头节点 head = node # 将新加入链表节点设置为头节点 return head # 要遍历链表需要从头往回找 def create_linklist_tail(li): """尾插法创建链表""" head = Node(li[ 0 ]) # 创建头节点对象 tail = head # 尾节点也是头节点 for element in li[ 1 :]: # 从第二个到最后一个遍历列表 node = Node(element) # 创建一个新链表节点 tail. next = node # 设置实例next属性指向链表尾节点 tail = node # 将新加入链表的节点设置为尾节点 return head # 返回头节点,可以从头往回找 def print_linklist(lk): """打印链表""" while lk: # 只要lk存在 print (lk.item, end = ',' ) # 打印链表值 lk = lk. next # 到最后一个节点的时候,lk.next属性为空退出循环 lk = create_linklist_head([ 1 , 2 , 3 ]) print (lk) print_linklist(lk) """ <__main__.Node object at 0x10402de48> 3,2,1, """ lk2 = create_linklist_tail([ 1 , 3 , 6 , 8 , 9 ]) print_linklist(lk2) """ 3,2,1,1,3,6,8,9, """ |
3、链表的遍历
4、链表节点的插入和删除(视频缺,需要补)
二、双链表
双链表的每个节点有两个指针:一个指向后一个节点,另一个指向前一个节点。
1、节点定义
1 2 3 4 5 | class Node( object ): def __init__( self , item): self .item = item # 数据 self . next = None # 指针,指向后一个节点 self .prior = None # 指针,指向前一个节点 |
2、双链表节点插入
1 2 3 4 5 | #插入 p. next = curNode. next curNode. next .prior = p p.prior = curNode curNode. next = p |
代码过程图示如下:
(1)p.next = curNode.next 让p.next指针指向curNode下一个节点
(2)curNode.next.prior = p 让curNode下一个节点的prior指针指向p
(3)p.prior = curNode 让p的prior指针指向curNode
(4)curNode.next = p 让curNode的next指针指向p
3、双链表节点的删除
1 2 3 4 5 | #删除 p = curNode. next curNode. next = p. next p. next .prior = curNode del p |
代码过程图示如下:
(1)p = curNode.next 指定要删除的节点是curNode的next指针指向的节点
(2)curNode.next = p.next 修改curNode的next指针指向要删除节点的next指针指向的节点
(3)p.next.prior = curNode 修改p的next指针指向的节点的prior指针,将指针指向curNode
(4)del p 删除p
三、链表总结
1、顺序表(列表)与 链表复杂度对比分析
按元素值查找时:都是挨个查看,时间复杂度都为O(n)
按下标查找时:顺序表更快,直接插入到对位位置。链表则需要从头开始数。链表时间复杂度是O(n),顺序表时间复杂度是O(1)。
在某元素后插入:这种情况顺序表是O(n),插入后,后面的元素都需要往后挪。链表则是O(1)。
删除某元素:这种情况顺序表是O(n),删除后,后面的元素都需要往前挪。链表则是O(1)。
2、链表和顺序表对比总结
链表在插入和删除操作上明显快于顺序表。
链表的内存可以更灵活的分配。java等的数组一开始申请的空间如果满了是没有办法解决的,python的列表在一开始申请的空间不足时,也是通过重新申请新的空间,将原来内存空间的内容拷贝进去。
可以尝试利用链表重新实现栈和队列,使用栈实现队列就不用考虑队满的问题,也不用设计为环形。
链表这种链式存储的数据结构对树和图的结构有很大的启发性。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术