Python:说说字典和散列表,散列冲突的解决原理
散列表
Python 用散列表来实现 dict。散列表其实是一个稀疏数组(总是有空白元素的数组称为稀疏数组)。在一般书中,散列表里的单元通常叫做表元(bucket)。在 dict 的散列表当中,每个键值对都占用一个表元,每个表元都有两个部分,一个是对键的引用,一个是对值的引用。因为每个表元的大小一致,所以可以通过偏移量来读取某个表元。
Python 会设法保证大概还有三分之一的表元是空的,当快要达到这个阀值的时候,会进行扩容,将原散列表复制到一个更大的散列表里。
如果要把一个对象放入到散列表里,就先要计算这个元素键的散列值。这就要求键(key)必须是可散列的。
一个可散列的对象必须满足以下条件:
- 支持
hash()
函数,并且通过hash()
方法所得到的散列值是不变的。 - 支持通过
eq()
方法来检测相等性。 - 若
a == b
为真,则hash(a) == hash(b)
也为真。
散列表的算法:
为了获取键 search_key
所对应的值 search_value
,Python 会首先调用 hash(search_key)
计算 search_key
的散列值,把这个值最低的几位数字当作偏移量,在散列表里查找表元(具体取几位,得看当前散列表的大小)。若找到的表元是空的,则抛出 KeyError
异常;若不为空,则表元里会有一对 found_key
:found_value
,检验 search_key
和 found_key
是否相等,若相等,则返回 found_value
。若不相等,这种情况称为散列冲突。
为了解决散列冲突,算法会在散列值中另外再取几位,然后用特殊的方法处理一下,把得到的新数值作为偏移量在散列表中查找表元,若找到的表元是空的,则同样抛出 KeyError 异常;若非空,则比较键是否一致,一致则返回对应的值;若又发现散列冲突,则重复以上步骤。
添加新元素跟上面的过程几乎一样,只不过在发现空表元的时候会放入这个新元素,不为空则为散列冲突,继续查找。
为什么字典是无序的
当往 dict 里添加新元素并且发生了散列冲突的时候,新元素可能会被安排存放到另一个位置。于是就会发生下面的情况:dict([key1, value1], [key2, value2])
和 dict([key2, value2], [key1, value1])
两个字典,在进行比较的时候是相等的,但如果 key1 和 key2 散列冲突,则这两个键在字典里的顺序是不一样的(因为添加的顺序不一样,先添加的先占据第一次散列值的位置,后添加的)。
无论何时,往 dict 里添加新的键,Python 解析器都可能做出为字典扩容的决定。扩容导致的结果就是要新建一个更大的散列表,并把字典里已有的元素添加到新的散列表里。这个过程中可能发生新的散列冲突,导致新散列表中键的次序变化。
如果在迭代一个字典的同时往里面添加新的键,会发生什么?不凑巧扩容了,不凑巧键的次序变了,然后就 orz 了。
总结
散列表是一个在时间和空间上做出权衡的经典例子。如果没有空间(内存)的限制,那么可以直接将键作为数组的索引。那么所有的查找时间复杂度为 O(1);如果没有时间的限制,那么可以直接用数组,这样只需要很少的内存。
Python:说说字典和散列表,散列冲突的解决原理的更多相关文章
- python中字典排序,列表中的字典排序
python中字典排序,列表中的字典排序 一.使用python模块:operator import operator #首先要导入模块operator x = {1:2, 3:4, 4:3, 2:1, ...
- 【Java集合学习】HashMap源码之“拉链法”散列冲突的解决
1.HashMap的概念 HashMap 是一个散列表,它存储的内容是键值对(key-value)映射. HashMap 继承于AbstractMap,实现了Map.Cloneable.java.io ...
- Python中将字典转换为有序列表、无序列表的方法
说明:列表不可以转换为字典 1.转换后的列表为无序列表 a = {'a' : 1, 'b': 2, 'c' : 3} #字典中的key转换为列表 key_value = list(a.keys()) ...
- python获取字典的key列表
获取字典的所有key: # !/usr/bin/python3.4 # -*- coding: utf-8 -*- b = { 'video':0, 'music':23 } print(list(b ...
- Python与数据结构[4] -> 散列表[1] -> 分离链接法的 Python 实现
分离链接法 / Separate Chain Hashing 前面完成了一个基本散列表的实现,但是还存在一个问题,当散列表插入元素冲突时,散列表将返回异常,这一问题的解决方式之一为使用链表进行元素的存 ...
- 【阅读笔记:散列表】Javascript任何对象都是一个散列表(hash表)!
什么是散列表? 散列表是Dictionary(字典)的一种散列表实现方式,字典传送门 一个很常见的应用是使用散列表来表示对象.Javascript语言内部就是使用散列表来表示每个对象.此时,对象的每个 ...
- 散列表(拉链法与线性探测法)Java实现
package practice; import java.security.Principal; import java.util.Scanner; import edu.princeton.cs. ...
- 散列表(Hash Table)
散列表(hash table): 也称为哈希表. 根据wikipedia的定义:是根据关键字(Key value)而直接访问在内存存储位置的数据结构.也就是说,它通过把键值通过一个函数的计算,映射到表 ...
- HashMap、lru、散列表
HashMap HashMap的数据结构:HashMap实际上是一个数组和链表("链表散列")的数据结构.底层就是一个数组结构,数组中的每一项又是一个链表. hashCode是一个 ...
随机推荐
- lease.go
package ) type:]...) :]...) )*time.Second) ) go func() { select { case <-stop ...
- .net core微服务之基于Docker+Consul+Registrator服务注册服务发现
一.Docker部分: 先拉最新的asp.net core的镜像: docker pull microsoft/aspnetcore 将下载下来的镜像重命名,为什么要重命名?等会讲Registrato ...
- Java并发编程(五)锁的使用(下)
显式锁 上篇讲了使用synchronized关键字来定义锁,其实Java除了使用这个关键字外还可以使用Lock接口及其实现的子类来定义锁,ReentrantLock类是Lock接口的一个实现,Reen ...
- b2OJ_1565_[NOI2009]植物大战僵尸_拓扑排序+最大权闭合子图
b2OJ_1565_[NOI2009]植物大战僵尸_拓扑排序+最大权闭合子 题意:n*m个植物,每个植物有分数(可正可负),和能保护植物的位置.只能从右往左吃,并且不能吃正被保护着的,可以一个不吃,求 ...
- 在CentOS下安装Git
1.安装git依赖包 yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel gcc perl-ExtUti ...
- 浅析ajax原理与用法
1 ajax原理 Ajax(Asynchronous JavaScript and XML (异步的JavaScript和XML)),是一种快速创建 动态网页的技术,目的是显示动态局部刷新.通过XML ...
- Android6.0 源码修改之 Contacts应用
一.Contacts应用的主界面和联系人详情界面增加顶部菜单添加退出按钮 通过Hierarchy View 工具可以发现 主界面对应的类为 PeopleActivity 联系人详情界面对应的类为 Qu ...
- Shell编程(week4_day5)--技术流ken
本节内容 1. 三剑客简介 2. sed命令详解 3. awk命令详解 文本处理三剑客 在 Shell 下使用这些正则表达式处理文本最多的命令有下面几个工具: 命令 描述 ...
- HighChar 案例
Highchars //前台 <script> $(function () { //showChat(); initChat(); showPie(); initPie(); }) fun ...
- Linq中 AsQueryable(), AsEnumerable()和ToList()的区别和用法
Linq中 AsQueryable(), AsEnumerable()和ToList()的区别和用法:在写LINQ语句的时候,往往会看到AsEnumerable() ,AsQueryable() 和T ...