当前位置: 首页 > news >正文

玩转python: 掌握Python数据结构之链表

链表是计算机科学中最基础的数据结构之一,也是许多高级数据结构和算法的基础。本文将带你从零开始,逐步掌握链表的概念、实现和应用。通过丰富的案例和通俗易懂的解释,你将能够轻松理解并应用链表。

什么是链表?

链表是一种线性数据结构,由一系列节点(Node)组成。每个节点包含两个部分:数据域和指针域。数据域用于存储数据,指针域用于指向下一个节点。链表中的节点通过指针连接在一起,形成一个链式结构。

与数组不同,链表的内存空间不需要连续分配。这意味着链表可以更灵活地利用内存空间,但也意味着访问链表中的元素需要从头节点开始逐个遍历。

链表的类型

链表有多种类型,常见的有:

  1. 单链表(Singly Linked List):每个节点只有一个指针,指向下一个节点。
  2. 双链表(Doubly Linked List):每个节点有两个指针,一个指向前一个节点,一个指向下一个节点。
  3. 循环链表(Circular Linked List):尾节点的指针指向头节点,形成一个环。

本文将重点介绍单链表,因为它是最基础且最常用的链表类型。

单链表的实现

在Python中,我们可以通过定义一个Node类和一个LinkedList类来实现单链表。

1. 定义节点类

首先,我们需要定义一个节点类Node,它包含数据域和指针域。

class Node:def __init__(self, data):self.data = data  # 数据域self.next = None  # 指针域,初始化为None

2. 定义链表类

接下来,我们定义一个链表类LinkedList,它包含对链表的各种操作,如插入、删除、遍历等。

class LinkedList:def __init__(self):self.head = None  # 头节点,初始化为None# 在链表末尾插入节点def append(self, data):new_node = Node(data)if self.head is None:  # 如果链表为空,新节点为头节点self.head = new_nodereturnlast_node = self.headwhile last_node.next:  # 遍历到链表末尾last_node = last_node.nextlast_node.next = new_node  # 将新节点插入到末尾# 在链表头部插入节点def prepend(self, data):new_node = Node(data)new_node.next = self.head  # 新节点的指针指向当前头节点self.head = new_node  # 更新头节点为新节点# 删除指定数据的节点def delete(self, data):current_node = self.headif current_node and current_node.data == data:  # 如果头节点就是要删除的节点self.head = current_node.nextcurrent_node = Nonereturnprev_node = Nonewhile current_node and current_node.data != data:  # 遍历查找要删除的节点prev_node = current_nodecurrent_node = current_node.nextif current_node is None:  # 如果没找到要删除的节点returnprev_node.next = current_node.next  # 跳过要删除的节点current_node = None# 打印链表def print_list(self):current_node = self.headwhile current_node:print(current_node.data, end=" -> ")current_node = current_node.nextprint("None")

3. 使用链表

现在,我们可以使用LinkedList类来创建和操作链表。

# 创建一个链表
llist = LinkedList()# 在链表末尾插入节点
llist.append(1)
llist.append(2)
llist.append(3)# 在链表头部插入节点
llist.prepend(0)# 打印链表
llist.print_list()  # 输出: 0 -> 1 -> 2 -> 3 -> None# 删除节点
llist.delete(2)# 打印链表
llist.print_list()  # 输出: 0 -> 1 -> 3 -> None

链表的应用案例

链表在实际应用中有很多用途,以下是几个常见的案例:

1. 实现队列

队列是一种先进先出(FIFO)的数据结构,链表可以很方便地实现队列。

class Queue:def __init__(self):self.linked_list = LinkedList()# 入队def enqueue(self, data):self.linked_list.append(data)# 出队def dequeue(self):if self.linked_list.head is None:return Nonedata = self.linked_list.head.dataself.linked_list.delete(data)return data# 打印队列def print_queue(self):self.linked_list.print_list()# 使用队列
queue = Queue()
queue.enqueue(1)
queue.enqueue(2)
queue.enqueue(3)
queue.print_queue()  # 输出: 1 -> 2 -> 3 -> None
print(queue.dequeue())  # 输出: 1
queue.print_queue()  # 输出: 2 -> 3 -> None

2. 实现栈

栈是一种后进先出(LIFO)的数据结构,链表也可以很方便地实现栈。

class Stack:def __init__(self):self.linked_list = LinkedList()# 入栈def push(self, data):self.linked_list.prepend(data)# 出栈def pop(self):if self.linked_list.head is None:return Nonedata = self.linked_list.head.dataself.linked_list.delete(data)return data# 打印栈def print_stack(self):self.linked_list.print_list()# 使用栈
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
stack.print_stack()  # 输出: 3 -> 2 -> 1 -> None
print(stack.pop())  # 输出: 3
stack.print_stack()  # 输出: 2 -> 1 -> None

3. 反转链表

反转链表是一个经典的面试题,我们可以通过修改指针的方向来实现。

def reverse_linked_list(llist):prev_node = Nonecurrent_node = llist.headwhile current_node:next_node = current_node.next  # 保存下一个节点current_node.next = prev_node  # 反转指针prev_node = current_node  # 移动prev_nodecurrent_node = next_node  # 移动current_nodellist.head = prev_node  # 更新头节点# 反转链表
llist = LinkedList()
llist.append(1)
llist.append(2)
llist.append(3)
llist.print_list()  # 输出: 1 -> 2 -> 3 -> None
reverse_linked_list(llist)
llist.print_list()  # 输出: 3 -> 2 -> 1 -> None

4. 链表实现LRU缓存

LRU(Least Recently Used)缓存是一种常见的缓存淘汰策略,它会优先淘汰最近最少使用的数据。链表可以很好地支持LRU缓存的实现。

案例:浏览器缓存

假设你正在浏览网页,浏览器会将你最近访问的网页缓存起来。当你访问一个新网页时,如果缓存已满,浏览器会淘汰最久未访问的网页缓存。这个过程可以用链表来实现。

class LRUCache:def __init__(self, capacity):self.capacity = capacity  # 缓存容量self.cache = {}  # 存储缓存数据self.linked_list = LinkedList()  # 使用链表记录访问顺序# 获取缓存数据def get(self, key):if key in self.cache:# 如果缓存中有该数据,将其移到链表头部(表示最近使用)self.linked_list.delete(key)self.linked_list.prepend(key)return self.cache[key]return -1  # 如果缓存中没有该数据,返回-1# 插入缓存数据def put(self, key, value):if key in self.cache:# 如果缓存中已有该数据,更新其值并移到链表头部self.linked_list.delete(key)elif len(self.cache) >= self.capacity:# 如果缓存已满,淘汰链表尾部的数据(最久未使用)last_key = self.linked_list.headwhile last_key.next:last_key = last_key.nextself.linked_list.delete(last_key.data)del self.cache[last_key.data]# 将新数据插入链表头部self.linked_list.prepend(key)self.cache[key] = value# 使用LRU缓存
cache = LRUCache(2)
cache.put(1, "网页1")
cache.put(2, "网页2")
print(cache.get(1))  # 输出: 网页1
cache.put(3, "网页3")  # 缓存已满,淘汰网页2
print(cache.get(2))  # 输出: -1(网页2已被淘汰)
通俗解释:
  • 链表记录了数据的访问顺序,最近访问的数据在链表头部,最久未访问的数据在链表尾部。
  • 当缓存满时,直接淘汰链表尾部的数据,确保缓存中始终保留最近使用的数据。

5. 链表实现多项式相加

多项式相加是数学中常见的操作,链表可以用来表示多项式,并实现相加功能。

案例:多项式计算

假设有两个多项式:

  • 多项式A:3x^2 + 2x + 5
  • 多项式B:4x^3 + 2x^2 + 1

我们可以用链表表示这两个多项式,并实现它们的相加。

class PolynomialTerm:def __init__(self, coefficient, exponent):self.coefficient = coefficient  # 系数self.exponent = exponent  # 指数self.next = None  # 指向下一个项的指针class Polynomial:def __init__(self):self.head = None  # 多项式的头节点# 添加项def add_term(self, coefficient, exponent):new_term = PolynomialTerm(coefficient, exponent)if self.head is None:self.head = new_termelse:current = self.headwhile current.next:current = current.nextcurrent.next = new_term# 打印多项式def print_polynomial(self):current = self.headwhile current:print(f"{current.coefficient}x^{current.exponent}", end=" + " if current.next else "\n")current = current.next# 多项式相加def add_polynomials(poly1, poly2):result = Polynomial()current1 = poly1.headcurrent2 = poly2.headwhile current1 and current2:if current1.exponent > current2.exponent:result.add_term(current1.coefficient, current1.exponent)current1 = current1.nextelif current1.exponent < current2.exponent:result.add_term(current2.coefficient, current2.exponent)current2 = current2.nextelse:result.add_term(current1.coefficient + current2.coefficient, current1.exponent)current1 = current1.nextcurrent2 = current2.next# 将剩余项加入结果while current1:result.add_term(current1.coefficient, current1.exponent)current1 = current1.nextwhile current2:result.add_term(current2.coefficient, current2.exponent)current2 = current2.nextreturn result# 创建多项式A:3x^2 + 2x + 5
polyA = Polynomial()
polyA.add_term(3, 2)
polyA.add_term(2, 1)
polyA.add_term(5, 0)# 创建多项式B:4x^3 + 2x^2 + 1
polyB = Polynomial()
polyB.add_term(4, 3)
polyB.add_term(2, 2)
polyB.add_term(1, 0)# 相加并打印结果
result = Polynomial.add_polynomials(polyA, polyB)
result.print_polynomial()  # 输出: 4x^3 + 5x^2 + 2x + 6
通俗解释:
  • 每个链表节点表示多项式的一个项,包含系数和指数。
  • 通过遍历两个链表,将相同指数的项相加,最终得到一个新的多项式。

6. 链表实现约瑟夫问题

约瑟夫问题是一个经典的数学问题,描述如下:n个人围成一圈,从第k个人开始报数,数到m的人出列,直到所有人出列。链表可以很好地模拟这个过程。

案例:游戏中的淘汰机制

假设你和朋友们围成一圈玩“数到3就淘汰”的游戏,链表可以模拟这个过程。

def josephus(n, k, m):# 创建循环链表class Node:def __init__(self, data):self.data = dataself.next = Nonehead = Node(1)prev = headfor i in range(2, n + 1):new_node = Node(i)prev.next = new_nodeprev = new_nodeprev.next = head  # 形成环# 找到第k个人current = headfor _ in range(k - 1):current = current.next# 开始报数并淘汰while current.next != current:# 报数到m-1for _ in range(m - 1):current = current.next# 淘汰第m个人print(f"淘汰:{current.next.data}")current.next = current.next.nextcurrent = current.nextprint(f"最后剩下:{current.data}")# 使用约瑟夫问题
josephus(5, 1, 3)  # 输出: 淘汰:3 -> 淘汰:1 -> 淘汰:5 -> 淘汰:2 -> 最后剩下:4
通俗解释:
  • 链表模拟了一个圆圈,每个人是一个节点,节点的指针指向下一个人。
  • 从第k个人开始报数,数到m的人被淘汰(从链表中移除),直到最后剩下一个人。

总结

通过以上案例,我们可以看到链表在实际问题中的广泛应用。无论是缓存管理、数学计算还是游戏模拟,链表都能提供高效的解决方案。希望这些案例能帮助你更好地理解链表的应用场景,并激发你进一步探索数据结构的兴趣!

相关文章:

玩转python: 掌握Python数据结构之链表

链表是计算机科学中最基础的数据结构之一&#xff0c;也是许多高级数据结构和算法的基础。本文将带你从零开始&#xff0c;逐步掌握链表的概念、实现和应用。通过丰富的案例和通俗易懂的解释&#xff0c;你将能够轻松理解并应用链表。 什么是链表&#xff1f; 链表是一种线性…...

upload-labs详解(1-12)文件上传分析

目录 uploa-labs-main upload-labs-main第一关 前端防御 绕过前端防御 禁用js Burpsuite抓包改包 upload-labs-main第二关 上传测试 错误类型 upload-labs-env upload-labs-env第三关 上传测试 查看源码 解决方法 重命名&#xff0c;上传 upload-labs-env第四关…...

RAG系统(检索增强生成)的优化策略

RAG(检索增强生成)系统的优化可以从多个方面入手,主要包括数据、查询、检索、生成、框架和评估等几个重要环节。本文将详细介绍这些优化策略,并为每个环节提供具体的操作方法。 一、数据优化 1. 数据清洗和增强 数据质量直接影响检索和生成的效果,因此需要进行细致的数据…...

写毕业论文用哪个AI好?这6款AIGC论文工具给你答案

撰写毕业论文是一项艰巨的任务&#xff0c;AIGC 论文工具的出现为同学们提供了有力支持。以下 6 款工具在功能、适用场景等方面各有优势&#xff0c;助你高效完成毕业论文。 文赋 AI 论文 文赋 AI 论文堪称毕业论文写作的得力助手。它的生成速度令人惊叹&#xff0c;短短 5 分…...

loadingcache优化

问题分析 通过当前现场的火焰图进行分析 原本的loadingcache public LoadingCache<Integer, Student> map Caffeine.newBuilder().refreshAfterWrite(CONTRACT_CACHE_HOURS, TimeUnit.HOURS).maximumSize(CONTRACT_CONFIG_CACHE_SIZE).recordStats().build(key -> …...

【Vue3 Element UI - Plus + Tyscript 实现Tags标签输入及回显】

Vue3 Element Plus TypeScript 实现 Tags 标签输入及回显 在开发后台管理系统或表单页面时&#xff0c;动态标签&#xff08;Tags&#xff09; 是一个常见的功能需求。用户可以通过输入框添加标签&#xff0c;并通过关闭按钮删除标签&#xff0c;同时还需要支持标签数据的提…...

STM32 子设备通过CAN发送数据到主设备

采集ADC、GPS经纬坐标、温湿度数据、大气压数据通过CAN方式发送给主设备端&#xff0c;帧ID按照如下定义&#xff1a; 我尼玛一个标准帧ID位数据是11位&#xff0c;扩展帧才是111829位&#xff0c;它说最开头的是四位是真类型&#xff0c;并给我如下解释&#xff1a; 它把帧的定…...

Python可视化——地理空间型图表(自用)

地图信息可视化的实现就是将不可展开的曲面上的地理坐标信息转化为二维平面进行显示&#xff0c;这个过程也叫地图投影&#xff08;空间三维投影到平面二维&#xff09; 地图投影的要求&#xff1a;等面积、等角度、等距离。总的来说就是映射到二维平面中的任何点通过比例尺放大…...

WordPress报502错误问题解决-php-fpm-84.service loaded failed failed LSB: starts php-fpm

文章目录 问题描述问题排查问题解决 问题描述 服务器环境&#xff1a; php&#xff1a;8.4MySQL&#xff1a;8.0Nginx&#xff1a;1.26.2 在访问站点时&#xff0c;一直报502&#xff0c;而两天前还能正常访问。 问题排查 导致502的问题很多&#xff0c;比如站点访问量太大…...

Python在SEO中的自动化应用爬虫开发与日志分析实例

引言 搜索引擎优化&#xff08;SEO&#xff09;是数字营销中至关重要的一环&#xff0c;旨在提高网站在搜索引擎结果页面&#xff08;SERP&#xff09;中的排名。随着互联网数据的爆炸式增长&#xff0c;手动进行SEO分析和管理变得愈发困难。Python作为一种强大的编程语言&…...

thingsboard edge 在windows 环境下的配置

按照官方文档&#xff1a;Installing ThingsBoard Edge on Windows | ThingsBoard Edge&#xff0c;配置好java环境和PostgreSQL。 下载对应的windows 环境下的tb-edge安装包。下载附件 接下来操作具体如下 步骤1&#xff0c;需要先在thingsboard 服务上开启edge 权限 步骤2…...

nnMamba:基于状态空间模型的3D生物医学图像分割、分类和地标检测

摘要 本文提出了一种基于状态空间模型&#xff08;SSMs&#xff09;的创新架构——nnMamba&#xff0c;用于解决3D生物医学图像分割、分类及地标检测任务中的长距离依赖建模难题。nnMamba结合了卷积神经网络&#xff08;CNN&#xff09;的局部特征提取能力与SSMs的全局上下文建…...

nginx 配置403页面(已亲测)

问题&#xff1a;GET请求访问漏洞url即可看到泄露的内网ip 解决方式&#xff1a; 1.配置nginx 不显示真实Ip 2.限制接口只能是POST请求 具体配置&#xff1a; 编写一个403.html 在nginx的配置文件中&#xff0c;配置location参数&#xff1a; location /api/validationCode…...

SyntaxError: Invalid or unexpected token in JSON at position x

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 &#x1f35a; 蓝桥云课签约作者、…...

Uncaught TypeError: Cannot read properties of undefined (reading ‘xxx‘)

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 &#x1f35a; 蓝桥云课签约作者、…...

Nginx 跨域配置详细讲解

一、跨域请求概述 跨域资源共享&#xff08;CORS&#xff0c;Cross-Origin Resource Sharing&#xff09;是一种机制&#xff0c;它使用额外的HTTP头部来告诉浏览器让运行在一个origin&#xff08;域&#xff09;上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资…...

前端开发基石:HTML语义化深度解析与实践指南

一、语义化设计的本质价值 1.1 从文档结构到信息表达 在Web诞生初期&#xff08;1991年&#xff09;&#xff0c;HTML仅包含18个标签用于学术文档展示。经过30年发展&#xff0c;HTML5已拥有超过110个标签&#xff0c;其中语义化标签占比提升至60%。这种演进背后是互联网从简…...

mongodb安装教程以及mongodb的使用

MongoDB是由C语言编写的一种面向文档的NoSQL数据库&#xff0c;旨在为WEB应用提供可扩展的高性能数据存储解决方案。与传统的关系型数据库&#xff08;如 MySQL 或 PostgreSQL&#xff09;不同&#xff0c;MongoDB 存储数据的方式是以 BSON&#xff08;类似于 JSON 的二进制格式…...

C# 中的多线程同步机制:lock、Monitor 和 Mutex 用法详解

在多线程编程中&#xff0c;线程同步是确保多个线程安全地访问共享资源的关键技术。C# 提供了几种常用的同步机制&#xff0c;其中 lock、Monitor 和 Mutex 是最常用的同步工具。本文将全面介绍这三种同步机制的用法、优缺点以及适用场景&#xff0c;帮助开发者在多线程开发中做…...

【通义万相】蓝耘智算 | 开源视频生成新纪元:通义万相2.1模型部署与测评

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈人工智能与大模型应用 ⌋ ⌋ ⌋ 人工智能&#xff08;AI&#xff09;通过算法模拟人类智能&#xff0c;利用机器学习、深度学习等技术驱动医疗、金融等领域的智能化。大模型是千亿参数的深度神经网络&#xff08;如ChatGPT&…...

期权帮|中证1000股指期权交割结算价怎么算?

期权帮锦鲤三三每日分享期权知识&#xff0c;帮助期权新手及时有效地掌握即市趋势与新资讯&#xff01; 中证1000股指期权交割结算价怎么算&#xff1f; 一、按照最后交易日结算价&#xff1a; &#xff08;1&#xff09;计算方法&#xff1a;最后交易日标的指数&#xff08…...

Python 面向对象高级编程-定制类

目录 __str__ __iter__ __getitem__ __getattr__ __call__ 小结 看到类似__slots__这种形如__xxx__的变量或者函数名就要注意&#xff0c;这些在Python中是有特殊用途的。 __slots__我们已经知道怎么用了&#xff0c;__len__()方法我们也知道是为了能让class作用于len()…...

qt creator示例空白

通常情况下&#xff0c;进入qt后&#xff0c;就会弹出以下窗口&#xff1a; 但如果出现示例空白&#xff0c;那可能是因为 Qt Creator 无法正确识别 Qt 的安装路径或配置。 解决&#xff1a; 点击“添加”&#xff1a; 然后跳转到你的qmake.exe的目录&#xff0c;例如我的qmak…...

MyBatis-Plus 与 Spring Boot 的最佳实践

在现代 Java 开发中,MyBatis-Plus 和 Spring Boot 的结合已经成为了一种非常流行的技术栈。MyBatis-Plus 是 MyBatis 的增强工具,提供了许多便捷的功能,而 Spring Boot 则简化了 Spring 应用的开发流程。本文将探讨如何将 MyBatis-Plus 与 Spring Boot 进行整合,并分享一些…...

TDengine 中的标签索引

简介 本节说明 TDengine 的索引机制。在 TDengine 3.0.3.0 版本之前&#xff08;不含&#xff09;&#xff0c;默认在第一列 TAG 上建立索引&#xff0c;但不支持给其它列动态添加索引。从 3.0.3.0 版本开始&#xff0c;可以动态地为其它 TAG 列添加索引。对于第一个 TAG 列上…...

工业自动化核心:BM100 信号隔离器的强大力量

安科瑞 吕梦怡 18706162527 BM100系列信号隔离器可以对电流、电压等电量参数或温度、电阻等非电量参数进行快速精确测量&#xff0c;经隔 离转换成标准的模拟信号输出。既可以直接与指针表、数显表相接&#xff0c;也可以与自控仪表&#xff08;如PLC&#xff09;、各种 A/D …...

Ascend开发板镜像烧录、联网、其他设备访问

Ascend开发板镜像烧录、联网、外部访问 1.1 Ascend开发板制卡方式一&#xff1a;镜像烧录 SD卡插入读卡器&#xff0c;读卡器插入PC的USB接口 烧录镜像前&#xff0c;先格式化一下SD卡 参考教程&#xff1a;格式化SD卡、修复烧写系统失败的SD卡 WinR&#xff0c;输入cmd DIS…...

Llama-Factory框架下的Meta-Llama-3-8B-Instruct模型微调

目录 引言 Llama - Factory 训练框架简介&#xff1a; Meta - Llama - 3 - 8B - Instruct 模型概述&#xff1a; Lora 方法原理及优势&#xff1a; 原理 优势 环境准备: 部署环境测试&#xff1a; 数据准备&#xff1a; 模型准备&#xff1a; 模型配置与训练&#xff1…...

MySQL进阶-分析查询语句EXPLAIN

概述 能做什么&#xff1f; 表的读取顺序 数据读取操作的操作类型 哪些索引可以使用 哪些索引被实际使用 表之间的引用 每张表有多少行被优化器查询 官网介绍 https://dev.mysql.com/doc/refman/5.7/en/explain-output.html https://dev.mysql.com/doc/refman/8.0/…...

Python 高级编程与实战:构建数据可视化应用

在前几篇文章中,我们探讨了 Python 的基础语法、面向对象编程、函数式编程、元编程、性能优化、调试技巧、数据科学、机器学习、Web 开发、API 设计、网络编程、异步IO、并发编程、设计模式与软件架构、性能优化与调试技巧、分布式系统、微服务架构、自动化测试框架以及 RESTf…...