文章目录
Python内置了字典dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。
Python的dict采用了哈希表实现,最快能在O(1)时间内完成搜索。java的HashMap也是采用了哈希表实现。
不同之处是,dict在发生哈希冲突的时候采用了开放寻址法,而HashMap采用了链接法。
1.哈希表
直接寻址表:将 key 为 k 的元素,存放在索引为 k 的位置上
哈希表 :将 key 为 k 的元素,存放在索引为 h(k) 的位置上。
哈希表(Hash Table,又称为散列表)是一种线性表的存储结构。由一个直接寻址表 T (假设大小为m) 和一个哈希函数 h(k) 组成。对于任意可哈希对象,通过哈希函数(一般先进行哈希计算,然后对结果进行取余运算),将该对象映射为寻址表的索引 [0,1,2,…m-1],然后在该索引所对应的空间 T[0,1,2,…,m-1] 进行变量的存储/读取等操作。如下图所示。
Python 字典的底层实现是哈希表。调用内部的哈希函数,将键(Key)作为参数进行转换(哈希运算+取余运算),得到一个唯一的地址(地址的索引),然后将值(Value)存放到对应地址中(给相同的键赋值会直接覆盖原值,因为相同的键转换后的地址是一样的)。
例1:
例2:
Python中的字典: a = {‘name’: ‘Alex’, ‘age’: 18, ‘gender’: ‘Man’}
使用哈希表存储字典,通过哈希函数将字典的键映射为下标。
假设h(‘name’) = 3, h(‘age’) = 1, h(‘gender’)= 4,则哈希表存储为[None, 18, None, ’Alex’, ‘Man’]
注:键(Key)必须是可哈希的,即,通过哈希函数可为此键计算出唯一地址。
对于 Python 来说,变量,列表、字典、集合这些都是可变的,所以都不能做为键(Key)来使用。因为元祖里边可以存放列表这类可变因素,所以如果实在想拿元祖当字典的键(Key),那必须对元祖做限制:元组中只包括像数字和字符串这样的不可变元素时,才可以作为字典中有效的键(Key)。另外还需要注意的一点是,Python 的哈希算法对相同的值计算得到的结果是一样的,也就是说 12315 和 12315.0 的值相同,他们被认为是相同的键(Key)。
2.Python字典如何运用哈希表
插入: 对键进行哈希和取余运算,得到一个哈希表的索引,如果该索引所对应的表地址空间为空,将键值对存入该地址空间;
查询/更新: 对健进行哈希和取余运算,得到一个哈希表的索引,如果该索引所对应的地址空间中健与要查询/更新的健一致,那么就将该键值对取出来 / 更新该键所对应的值;
扩容: 字典初始化的时候,会对应初始化一个有k个空间的表,等空间不够用的时候,系统就会自动扩容,这时候会对已经存在的键值对 重新进行哈希取余运算(重新进行插入操作)保存到其它位置;
3.哈希冲突
由于哈希表的大小是有限的,而要存储的值的总数量是无限的,因此对于任何哈希函数,都会出现两个不同元素映射到同一个位置上的情况,这种情况叫做哈希冲突。字典使用了开放寻址法来解决冲突。
比如: h ( k ) = k m o d 7 , h ( 0 ) = h ( 7 ) = h ( 14 ) = . . . h(k)=k mod 7, h(0)=h(7)=h(14)=... h(k)=kmod7,h(0)=h(7)=h(14)=...
开放寻址法:如果哈希函数返回的位置已经有值,则可以探查新的空位置来存储这个值。
线性探查:如果位置i被占用,则探查 i + 1 i+1 i+1,