Python 十大数据结构
1.list
-
基本用法
-
使用场景
list 使用在需要查询,修改的场景,极不擅长需要频繁的插入,删除元素的场景
-
实现原理
list 对应数据结构的线性表,列表长度在初始状态无需指定,当插入元素超过初始长度后在启动动态扩容,时间复杂度O(n)
2.tuple
元组是一类不允许添加和删除元素的特殊列表,一旦创建不允许添加和删除修改
-
基本用法
元组大量使用在打包和解包处,如函数有多个返回值打包为一个元组,赋值到等号左侧变量时解包
t = 1,2,3 type(t) tuple
-
使用场景
相比于list,tuple实例更加节省内存,如果确定你的对象后面不会被修改,可以大胆使用元组。
# getsizeof 获取对象所占内存 from sys import getsizeof getsizeof(list())#56 getsizeof(tuple())#40
不同Python 版本得到的值可能不一样,我测试版本为Python3.9
-
实现原理
3.set
-
基本用法
set 是一种里面不能含有重复元素的数据结构,这种特性可以用来列表的去重
# 使用set 对list 去重 a = [1,2,3,4,5,1] set(a)# {1,2,3,4,5} # 使用 set 对set 做交集,并集,差集等操作 a = {1,2,3} b = {3,4,5} a.intersection(b) #{3}
-
使用场景
如果只是想缓存某些元素值,并且要求元素值不能重复时,可以使用此结构,并且set内部允许增删元素,且效率很高
-
实现原理
set 在内部将值哈希为索引,然后按照索引去获取数据,因此删除,增加,查询元素效率都很高
4.dict
-
基本用法
# 创建字典 d = {'a':1,'b':2} # 列表生成式 d = {a:b for a,b in zip(['a','b'],[1,2])} d #{'a':1,'b':2}
-
使用场景
字典适合在查询较多的场景,时间复杂度O(1),Python类中属性值等信息也是缓存在__dict__这个字典型数据结构中
from sys import getsizeof getsizeof(dict()) #232
dict占用字节数是list,tuple 的三四倍,对内存要求苛刻的场景谨慎使用字典
-
实现原理
字典是一种哈希表,同时保存了键值对
5.deque
deque 双端队列,基于list 优化了列表两端的增删数据的操作
-
基本用法
from collections import deque d = deque([3,2,4,0]) # 左侧移除元素 O(1) 时间复杂度 d.popleft() #3 # 左侧添加元素O(1)时间复杂度 d.appendleft(3) #3 d #deque([3,2,4,0])
-
使用场景
list 左侧添加删除元素的时间复杂度都为O(n),所以在Python中模拟队列是不要使用list,deque双端队列非常适合频繁在列表两端操作的场景,但是deque占用字节数特别大
In [15]: from sys import getsizeof In [16]: from collections import deque In [17]: getsizeof(deque) Out[17]: 408
-
实现原理
cpython 实现deque使用默认长度64的数组,每次从左侧移除一个元素,leftindex 加1,如果超过64就释放原来的内存块,在重新申请64长度的数组,并使用双端链表block管理内存块。
6.Counter
Counter 一种继承于dict用于统计元素个数的数据结构,也被称为bag或multiset
-
基本用法
In [18]: from collections import Counter In [19]: c = Counter([1,2,3,4,5,1,2,3]) In [20]: c Out[20]: Counter({1: 2, 2: 2, 3: 2, 4: 1, 5: 1}) # 统计第一最常见的项,返回元素及其次数的元组 In [21]: c.most_common(1) Out[21]: [(1, 2)]
-
使用场景
基本的dict能解决的问题就不要用Counter,但是遇到统计元素出现频次的场景,果断使用Counter
-
实现原理
Counter实现基于dict,它将元素存储于keys上,出现次数为values
7.OrderedDict
-
基本用法
继承于dict,能确保keys值按照顺序取出来的数据结构
In [22]: from collections import OrderedDict In [23]: od = OrderedDict({'c':3,'a':1,'b':2}) In [24]: for k,v in od.items(): ...: print(k,v) ...: c 3 a 1 b 2
-
使用场景
基本的dict无法保证顺序,keys映射为哈希值,而此值不是按照顺序存储在散列表中,所以遇到要确保字典keys有序场景,就要使用OrderedDict
-
实现原理
你一定会好奇OrderedDict如何确保keys顺序的,翻看cpython看到它里面维护着一个
双向链表self.__root ,它维护着keys的顺序。既然使用双向链表,细心的读者可能会有疑
问:删除键值对如何保证O(1)时间完成?
cpython使用空间换取时间的做法,内部维护一个self.__map 字典,键为key,值为指向双向链
表节点的link . 这样在删除某个键值对时,通过__map在O(1)内找到link,然后O(1)内从双向链
表__root中摘除。
8.heapq
基于list优化的一个数据结构:堆队列,也称为优先队列。堆队列特点在于最小的元素
总是在根结点
-
基本用法
In [25]: import heapq In [26]: a = [2,3,4,15,1] # 对 a 建堆,建堆完成后对a就地排序 In [27]: heapq.heapify(a) # 排序好的 a In [28]: a Out[28]: [1, 2, 4, 15, 3] In [29]: a[0] Out[29]: 1 # 最大的前两个元素 In [30]: heapq.nlargest(2,a) Out[30]: [15, 4] # 最小的前三个元素 In [31]: heapq.nsmallest(3,a) Out[31]: [1, 2, 3]
-
使用场景
如果要想要统计list中前几个最小(大)元素,使用heapq很方便,同时它还提供合并多个有序小list为大list的功能
-
实现原理
堆是一个二叉树,它的每个父节点的值都只会小于或大于所有的孩子节点的值
9.defaultdict
-
基本用法
In [34]: words=['book','nice','great','book'] In [35]: d ={} In [36]: for i,word in enumerate(words): ...: if word in d: ...: d[word].append(i) ...: else: ...: d[word] =[i] ...: In [37]: d Out[37]: {'book': [0, 3], 'nice': [1], 'great': [2]}
# 使用defaultdict In [38]: from collections import defaultdict In [39]: d = defaultdict(list) In [40]: for i,word in enumerate(words): ...: d[word]=i ...: In [41]: d Out[41]: defaultdict(list, {'book': 3, 'nice': 1, 'great': 2})
-
使用场景
适用于键的值必须指定一个默认值的场景,如键的值为list,set,dict
-
实现原理
调用工厂函数去提供确实的键的值
10.ChainMap
-
基本用法
如果有多个dict 想要合并成一个大的dict,那么ChainMap是你的选择,它的方便性体现在同步更改。
In [42]: from collections import ChainMap In [43]: d1 = {'a':1,'b':2,'c':3} In [44]: d2 = {'d':4,'e':5,'f':6} # 使用 ChainMap合并 d1 d2 In [45]: dm = ChainMap(d1,d2) In [46]: dm Out[46]: ChainMap({'a': 1, 'b': 2, 'c': 3}, {'d': 4, 'e': 5, 'f': 6}) In [48]: dm.maps Out[48]: [{'a': 1, 'b': 2, 'c': 3}, {'d': 4, 'e': 5, 'f': 6}] # 增加元素,体现在d1上 In [49]: dm.maps[0][2]=9 In [50]: dm.maps Out[50]: [{'a': 1, 'b': 2, 'c': 3, 2: 9}, {'d': 4, 'e': 5, 'f': 6}] In [51]: d1 Out[51]: {'a': 1, 'b': 2, 'c': 3, 2: 9} # 修改元素,体现在d1上 In [52]: dm.maps[0]['c']=9 In [53]: dm Out[53]: ChainMap({'a': 1, 'b': 2, 'c': 9, 2: 9}, {'d': 4, 'e': 5, 'f': 6}) In [54]: d1 Out[54]: {'a': 1, 'b': 2, 'c': 9, 2: 9}
-
使用场景
具体使用场景是我们有多个字典或者映射,想把他们合并成为一个单独的映射
使用update进行合并,会新建一个内存结构,除了浪费空间外,还有一个缺点,就是我们对新字典的更改不会同步到源字典上
-
实现原理
通过maps便能观察出ChainMap联合多个小dict装入list中,实际确实也是这样实现的,内部维护一个lis实例,其元素为小dict.