Python字典集合数据结构深入理解

来源:互联网 发布:高晓松酒驾 知乎 编辑:程序博客网 时间:2024/06/05 14:51

说明

在python中字dict和set是非常常用的两种数据结构,但是两种数据结构为什么要放在一起讨论。因为他们之所以拥有非常快的速度,是因为他们的内部结构都是散列表(散列表其实是一个稀疏数组总是有空白元素的数组称为稀疏数组)

dict中的散列表

  • 散列表算法

正常想要获取dict中的值,首先要知道key通过dict[key]获取对应的value,在散列表中为了达到这种操作,首先会计算key的hash值即散列值,把这个值最低的几位数字当作偏移量,在散列表里
查找表元(具体取几位,得看当前散列表的大小)。若找到表元为空,异常KeyError,不为空,表元里会有一对 found_key:found_value。这时候 Python 会检验 search_key == found_key 是否为真,如果它们相等的话,就会返回 found_value。如果两个值不匹配,则是散列冲突。

而散列表本
身的索引又只依赖于这个数字的一部分。为了解决散列冲突,算法会在散列值中另外
再取几位,然后用特殊的方法处理一下,把新得到的数字再当作索引来寻找表元。
若这次找到的表元是空的,则同样抛出 KeyError;若非空,或者键匹配,则返回这
个值;或者又发现了散列冲突,则重复以上的步骤。

image

另外在插入新值时,Python 可能会按照散列表的拥挤程度来决定是否要重新分配内存
为它扩容。如果增加了散列表的大小,那散列值所占的位数和用作索引的位数都会随
之增加,这样做的目的是为了减少发生散列冲突的概率。

Python 会设法保证大概还有三分之一的表元是空的,所以在快要达到这个阈值的时
候,原有的散列表会被复制到一个更大的空间里面。

  • dict的散列实现导致的结果

1.key必须是可hash的,所有不可变类型都是可哈希的故可作为键,可变类型不可哈希即不可作为键,如列表,字典类型。

2.在内存消耗上是巨大的,由于字典使用了散列表,而散列表又必须是稀疏的,这导致它在空间上的效率低下。

3.key查询很快,hash表空间换时间。

4.key的排列顺序,取决于添加顺序,并且当dict添加新数据,原有的排列可能会被打乱,因为Python 会设法保证大概还有三分之一的表元是空的,所以在快要达到这个阈值的时
候,原有的散列表会被复制到一个更大的空间里面。这时候重新hash导致排列顺序改变。

5.由此可知,不要对字典同时进行迭代和修改。如果想扫描并修改一个字典,最好分成
两步来进行:首先对字典迭代,以得出需要添加的内容,把这些内容放在一个新字典
里;迭代结束之后再对原有字典进行更新。

集合中的散列表

集合的实现和dict一样,集合的元素就相当于dict中的key,只不过集合没有散列表指向的value。
其特点:
- 集合里的元素必须是可散列的
- 集合很消耗内存
- 可以很高效地判断元素是否存在于某个集合
- 元素的次序取决于被添加到集合里的次序
- 往集合里添加元素,可能会改变集合里已有元素的次序

leason | 博客

原创粉丝点击