Python中常用的数据结构
用deque实现队列主要特点和优势:
高效的两端插入和删除操作:与列表相比,在队列的两端进行添加或删除元素的操作效率更高。时间复杂度为O(1),而在列表的开头进行插入或删除操作的时间复杂度通常为O(n),其中是列表的长度。
支持线程安全的追加和弹出:在多线程环境中,可以使用 deque 的 append 和 pop 方法,并且它们是线程安全的(需要适当的同步机制确保整体操作的线程安全)。
可设置最大长度:可以创建一个具有固定最大长度的 deque,当添加新元素超过最大长度时,会自动从另一端删除旧元素。
常用方法:
初始化:12d = collections.deque() # 创建一个空的双向队列。d = collections.deque([1, 2, 3]) # 用初始元素创建双向队列。
添加元素:12d.append(item) # 在队列右端添加一个元素。d.appendleft(item) # 在队列左端添加一个元素。
删除元素:12d.pop() # 删除并返回队列右端的元素。d.popleft() # 删除并返回队列左端的元素。
扩展队列:12d.ext ...
算法-动态规划
动态规划是一种解决多阶段决策过程最优化问题的数学方法。通常需要保存决策路径的问题用回溯法,而只是求最优解的时候选择动态规划。
基本概念
定义:动态规划通过把原问题分解为相对简单的子问题,并保存子问题的解,避免重复计算,从而高效地求解复杂问题。它通常适用于具有最优子结构和子问题重叠性质的问题。
最优子结构:一个问题具有最优子结构意味着问题的最优解可以由子问题的最优解组合而成。例如,在求解最短路径问题中,从起点到终点的最短路径可以由从起点到中间点的最短路径和从中间点到终点的最短路径组成。
子问题重叠:子问题重叠是指在求解问题的过程中,会多次重复地求解相同的子问题。动态规划通过保存子问题的解,避免了重复计算这些子问题,从而提高了算法的效率。
解题步骤
确定问题的状态:状态是描述问题在不同阶段的特征。选择合适的状态表示是动态规划的关键。例如,在背包问题中,可以用背包的剩余容量和已选物品的集合来表示状态。
定义状态转移方程:状态转移方程描述了如何从一个状态转移到另一个状态。它通常是根据问题的最优子结构性质推导出来的。例如,在背包问题中,状态转移方程可以表示为:dp[i][j] = max(dp ...
Python中的位图
在 Python 中,位图(Bitmap)是一种用于表示二进制数据的数据结构。它可以高效地存储和操作大量的布尔值(True/False)。
位图的基本概念位图通常由一个字节数组或位序列组成,其中每个位表示一个特定的状态或属性。例如,可以使用位图来表示一组整数是否存在于某个集合中,或者表示某个图形中的像素是否被选中。
Python 中实现位图的方法
使用内置的bytearray类型
bytearray是一个可变的字节序列,可以用来存储位图数据。每个字节可以表示 8 个位,通过位操作可以设置、清除和检查特定的位。例如:
12345bitmap = bytearray(10) # 创建一个可以存储 80 个位的位图# 设置第 5 个位为 1bitmap[5 // 8] |= 1 << (5 % 8)# 检查第 5 个位是否为 1is_set = (bitmap[5 // 8] & (1 << (5 % 8)))!= 0
使用第三方库bitarray库提供了一个更方便的位序列数据结构,可以高效地进行位操作。安装:pip install bitarray例 ...
Python中的collections
在 Python 中,collections是一个非常有用的内置模块,它提供了一些高性能的数据类型,可以帮助你更高效地处理数据。
namedtuple命名元组是一种可以为元组中的每个元素指定名称的数据类型。这使得代码更加可读,并且可以像访问对象属性一样访问元组的元素。
12345from collections import namedtuplePoint = namedtuple('Point', ['x', 'y'])p = Point(1, 2)print(p.x, p.y)
deque双端队列是一种可以在两端进行高效插入和删除操作的数据结构。它比列表更适合用于实现队列和栈。
123456from collections import dequedq = deque([1, 2, 3])dq.append(4)dq.appendleft(0)print(dq)
Counter计数器是一种用于统计可哈希对象出现次数的数据类型。它可以方便地统计列表、字符串等数据中的元素出现次数。
1234from collections ...
用环形缓冲区实现循环日志
环形缓冲区在数据结构中是一种特殊的线性数据结构,具有以下特点和优势:
结构特点
固定大小:环形缓冲区通常具有固定的容量,一旦创建,其大小就不能改变。这使得在内存使用方面更加可控,避免了动态分配内存带来的开销和不确定性。
循环利用空间:正因为其环形的特性,当写指针到达缓冲区的末尾时,会自动回绕到开头继续写入数据;读指针在读取完数据后也会相应地移动,实现空间的循环利用。
操作方式
写入操作:当向环形缓冲区写入数据时,首先检查缓冲区是否已满。如果未满,则将数据写入写指针所指的位置,然后写指针向前移动一位。如果写指针移动到缓冲区的末尾,它会回绕到开头。
读取操作:从环形缓冲区读取数据时,先检查缓冲区是否为空。如果不为空,则读取读指针所指位置的数据,然后读指针向前移动一位。同样,当读指针到达缓冲区的末尾时,会回绕到开头。
应用场景
实时数据处理:在需要对连续产生的实时数据进行处理的场景中,环形缓冲区可以作为数据的暂存区域。例如,音频和视频流的处理,数据采集系统等。
多生产者 - 多消费者模型:环形缓冲区可以方便地在多个生产者和多个消费者之间共享数据。生产者将数据写入缓冲区,消费者从缓冲区读取 ...
数据结构-链表
链表(Linked List)的基本概念链表是一种常见的数据结构,它由一系列节点(Node)组成。每个节点包含两部分:数据部分和指向下一个节点的指针(Pointer)。链表通过这些指针将节点按顺序连接在一起。
链表的类型
单链表(Singly Linked List):每个节点只有一个指针,指向下一个节点。
双链表(Doubly Linked List):每个节点有两个指针,一个指向前一个节点,一个指向下一个节点。
循环链表(Circular Linked List):单链表或双链表的最后一个节点的指针又指向了链表的头节点,形成一个环形结构。
链表的基本操作
创建链表:首先创建头节点(Head Node),然后逐个添加后续节点。
插入节点
在头部插入:创建新节点,将新节点的指针指向原头节点,然后更新头节点为新节点。
在中间插入:找到要插入位置的前一个节点,创建新节点,将新节点的指针指向插入位置的节点,然后将前一个节点的指针指向新节点。
在尾部插入:找到链表的尾节点,创建新节点,将尾节点的指针指向新节点,并将新节点的指针设为 None。
删除节点
删除头节点:将头节点指向下一个节 ...
数据结构-哈希表
哈希表(Hash Table)的基本概念哈希表是一种数据结构,它可以在平均情况下提供非常快速的插入、删除和查找操作。其基本思想是通过一个哈希函数将键(key)映射到一个特定的索引(index),然后将对应的值(value)存储在该索引位置。
哈希函数(Hash Function)
哈希函数是哈希表的核心,它将输入的键转换为数组的索引。例如,对于一个简单的整数键的哈希函数可以是hash(key) = key % array_size,这里array_size是存储数据的数组的大小。
一个好的哈希函数应该尽量减少冲突(Collision),即不同的键被映射到相同的索引位置。常见的哈希函数构造方法包括直接定址法、除留余数法、数字分析法、平方取中法、折叠法、随机数法等。
处理冲突(Collision Resolution)
链地址法(Chaining):当发生冲突时,在该索引位置创建一个链表,将新元素添加到链表中。这种方法简单且易于实现,但在最坏情况下,查找时间可能会退化到O(n),其中n是链表的长度。
开放定址法(Open Addressing):当发生冲突时,按照某种规则在哈希表中寻找下 ...
数据结构-栈
栈(Stack)的基本概念栈是一种重要的数据结构,它具有后进先出(Last In First Out,LIFO)的特点。可以把栈想象成一叠盘子,最后放上去的盘子会最先被拿走。
栈的基本操作
入栈(Push):将元素添加到栈的顶部。
出栈(Pop):从栈的顶部移除一个元素。
查看栈顶元素(Peek 或 Top):获取栈顶元素的值,但不移除它。
判断栈是否为空(IsEmpty):检查栈中是否有元素。
栈的应用场景
表达式求值:在算术表达式求值中,栈可以用于处理运算符的优先级和括号的匹配。例如,将中缀表达式转换为后缀表达式,然后利用栈进行求值。
函数调用栈:在程序执行过程中,当一个函数调用另一个函数时,当前函数的执行状态会被保存到一个栈中(函数调用栈)。当被调用函数执行完毕后,从栈中恢复上一个函数的执行状态继续执行。
括号匹配:在编译器和文本编辑器中,栈可以用于检查括号是否匹配。例如,在检查一段代码或者一个数学表达式中的括号是否成对出现且匹配正确时。
撤销(Undo)操作:在许多软件应用中,撤销操作可以通过栈来实现。每次执行一个操作,该操作的信息被压入栈中。当需要撤销时,从栈中弹出最近的 ...
数据结构-队列
队列(Queue)是一种常见的数据结构,它遵循先进先出(FIFO - First In First Out)的原则。
队列的基本操作
入队(Enqueue):在队列的尾部添加一个元素。
出队(Dequeue):从队列的头部移除一个元素。
查看队首元素(Peek):获取队列头部的元素,但不移除它。
判断队列是否为空(IsEmpty):检查队列中是否有元素。
队列的应用场景
广度优先搜索(BFS):在图的广度优先搜索算法中,队列用于存储待访问的节点。
任务调度:操作系统中的任务调度,例如按照先来先服务的原则处理进程。
消息传递系统:在消息队列中间件中,消息按照发送的先后顺序排队等待接收者处理。
Python中的queue模块在 Python 中,queue模块提供了同步的队列类,这些队列类可用于在多线程编程中安全地传递数据。以下是queue模块中主要的队列类及其基本用法:
Queue类
Queue类实现了一个基本的先进先出(FIFO)队列。
12345678910111213141516171819>>> import queue>>> q = ...
数据结构-前缀树
前缀树(Trie)简介前缀树,也称为字典树,是一种树形数据结构。它的核心思想是利用字符串的公共前缀来减少存储空间和提高查询效率。它由节点组成,每个节点包含多个指向子节点的指针(通常是一个固定大小的数组或者是一个哈希表),以及一个标记位用于表示从根节点到该节点是否构成一个完整的字符串。
工作原理
插入操作 从根节点开始,对于要插入的字符串中的每个字符,检查当前节点是否存在与该字符对应的子节点。 如果不存在,则创建一个新的子节点;如果存在,则沿着对应的子节点继续处理下一个字符。 当处理完字符串的最后一个字符后,将最后到达的节点的标记位置为表示一个完整字符串的结束。
查询操作 同样从根节点开始,按照待查询字符串的字符顺序依次在树中查找对应的子节点。 如果在某一步找不到对应的子节点,则说明该字符串不存在于树中;如果能顺利找到最后一个字符对应的节点且该节点的标记位表示是一个完整字符串的结束,那么说明该字符串存在于树中。
删除操作(相对复杂) 首先要找到要删除的字符串对应的节点路径。 如果该节点是其他字符串的中间节点,则不能直接删除,而是要清除该节点上表示字符串结束的标记位。 如果 ...