散列表

Python 用散列表来实现 dict散列表其实是一个稀疏数组(总是有空白元素的数组称为稀疏数组)。在一般书中,散列表里的单元通常叫做表元(bucket)。在 dict 的散列表当中,每个键值对都占用一个表元,每个表元都有两个部分,一个是对键的引用,一个是对值的引用。因为每个表元的大小一致,所以可以通过偏移量来读取某个表元。

Python 会设法保证大概还有三分之一的表元是空的,当快要达到这个阀值的时候,会进行扩容,将原散列表复制到一个更大的散列表里。

如果要把一个对象放入到散列表里,就先要计算这个元素键的散列值。这就要求键(key)必须是可散列的。

一个可散列的对象必须满足以下条件:

  • 支持 hash() 函数,并且通过 hash() 方法所得到的散列值是不变的。
  • 支持通过 eq() 方法来检测相等性。
  • a == b 为真,则 hash(a) == hash(b) 也为真。

散列表的算法:

为了获取键 search_key 所对应的值 search_valuePython 会首先调用 hash(search_key) 计算 search_key散列值,把这个值最低的几位数字当作偏移量,在散列表里查找表元(具体取几位,得看当前散列表的大小)。若找到的表元是空的,则抛出 KeyError 异常;若不为空,则表元里会有一对 found_key:found_value,检验 search_keyfound_key 是否相等,若相等,则返回 found_value。若不相等,这种情况称为散列冲突

为了解决散列冲突,算法会在散列值中另外再取几位,然后用特殊的方法处理一下,把得到的新数值作为偏移量在散列表中查找表元,若找到的表元是空的,则同样抛出 KeyError 异常;若非空,则比较键是否一致,一致则返回对应的值;若又发现散列冲突,则重复以上步骤。

添加新元素跟上面的过程几乎一样,只不过在发现空表元的时候会放入这个新元素,不为空则为散列冲突,继续查找。

为什么字典是无序的

当往 dict 里添加新元素并且发生了散列冲突的时候,新元素可能会被安排存放到另一个位置。于是就会发生下面的情况:dict([key1, value1], [key2, value2])dict([key2, value2], [key1, value1]) 两个字典,在进行比较的时候是相等的,但如果 key1key2 散列冲突,则这两个键在字典里的顺序是不一样的(因为添加的顺序不一样,先添加的先占据第一次散列值的位置,后添加的)。

无论何时,往 dict 里添加新的键,Python 解析器都可能做出为字典扩容的决定。扩容导致的结果就是要新建一个更大的散列表,并把字典里已有的元素添加到新的散列表里。这个过程中可能发生新的散列冲突,导致新散列表中键的次序变化。

如果在迭代一个字典的同时往里面添加新的键,会发生什么?不凑巧扩容了,不凑巧键的次序变了,然后就 orz 了。

总结

散列表是一个在时间和空间上做出权衡的经典例子。如果没有空间(内存)的限制,那么可以直接将键作为数组的索引。那么所有的查找时间复杂度为 O(1);如果没有时间的限制,那么可以直接用数组,这样只需要很少的内存。

Python:说说字典和散列表,散列冲突的解决原理的更多相关文章

  1. python中字典排序,列表中的字典排序

    python中字典排序,列表中的字典排序 一.使用python模块:operator import operator #首先要导入模块operator x = {1:2, 3:4, 4:3, 2:1, ...

  2. 【Java集合学习】HashMap源码之“拉链法”散列冲突的解决

    1.HashMap的概念 HashMap 是一个散列表,它存储的内容是键值对(key-value)映射. HashMap 继承于AbstractMap,实现了Map.Cloneable.java.io ...

  3. Python中将字典转换为有序列表、无序列表的方法

    说明:列表不可以转换为字典 1.转换后的列表为无序列表 a = {'a' : 1, 'b': 2, 'c' : 3} #字典中的key转换为列表 key_value = list(a.keys()) ...

  4. python获取字典的key列表

    获取字典的所有key: # !/usr/bin/python3.4 # -*- coding: utf-8 -*- b = { 'video':0, 'music':23 } print(list(b ...

  5. Python与数据结构[4] -> 散列表[1] -> 分离链接法的 Python 实现

    分离链接法 / Separate Chain Hashing 前面完成了一个基本散列表的实现,但是还存在一个问题,当散列表插入元素冲突时,散列表将返回异常,这一问题的解决方式之一为使用链表进行元素的存 ...

  6. 【阅读笔记:散列表】Javascript任何对象都是一个散列表(hash表)!

    什么是散列表? 散列表是Dictionary(字典)的一种散列表实现方式,字典传送门 一个很常见的应用是使用散列表来表示对象.Javascript语言内部就是使用散列表来表示每个对象.此时,对象的每个 ...

  7. 散列表(拉链法与线性探测法)Java实现

    package practice; import java.security.Principal; import java.util.Scanner; import edu.princeton.cs. ...

  8. 散列表(Hash Table)

    散列表(hash table): 也称为哈希表. 根据wikipedia的定义:是根据关键字(Key value)而直接访问在内存存储位置的数据结构.也就是说,它通过把键值通过一个函数的计算,映射到表 ...

  9. HashMap、lru、散列表

    HashMap HashMap的数据结构:HashMap实际上是一个数组和链表("链表散列")的数据结构.底层就是一个数组结构,数组中的每一项又是一个链表. hashCode是一个 ...

随机推荐

  1. WPF 列表开启虚拟化的方式

    正确开启虚拟化的方式 列表如ListBox,ListView,TreeView,GridView等,开启虚拟化 ScrollViewer设置CanContentScroll=True 直接在模板中,设 ...

  2. Mark一下~

    今天在cnblogs开通了博客,mark一下~ 上半年的Rebase阶段已经完成,希望下半年的Promotion阶段能收获满满,也希望自己能写出高质量的博客.

  3. Actor模型及原理

    1.Actor模型 在使用Java进行并发编程时需要特别的关注锁和内存原子性等一系列线程问题,而Actor模型内部的状态由它自己维护即它内部数据只能由它自己修改(通过消息传递来进行状态修改),所以使用 ...

  4. 基于ZigBee模块与51单片机之间的简化智能家居项目简介(学生版本)

    5月份学校举行比赛,我们团队报名<智能家居>的项目,设计的总体思路用:QT写的上位机与ZigBee无线通信加51作为终端的简易版智能家居 电路连接:PC机->cc2530(协调器)- ...

  5. Redis 缓存失效和回收机制续

    二.Redis Key失效机制 Redis的Key失效机制,主要借助借助EXPIRE命令: EXPIRE key 30 上面的命令即为key设置30秒的过期时间,超过这个时间,我们应该就访问不到这个值 ...

  6. 重学前端 --- Promise里的代码为什么比setTimeout先执行?

    首先通过一段代码进入讨论的主题 var r = new Promise(function(resolve, reject){ console.log("a"); resolve() ...

  7. 微服务(入门一):netcore安装部署consul

    环境准备  vs开发环境:vs2017 consul版本: 1.4.4 netcore版本:2.1 安裝Consul  1.从官网下载consul到本地,选择系统对应的版本进行下载到本地,下载地址:h ...

  8. <转载>Android性能优化之HashMap,ArrayMap和SparseArray

    本篇博客来自于转载,打开原文地址已经失效,在此就不贴出原文地址了,如原作者看到请私信我可用地址,保护原创,人人有责.   Android开发者都知道Lint在我们使用HashMap的时候会给出警告—— ...

  9. 使用Springboot + Gradle快速整合Mybatis-Plus

    使用Springboot + Gradle快速整合Mybatis-Plus 作者:Stanley 罗昊 [转载请注明出处和署名,谢谢!] MyBatis-Plus(简称 MP)是一个 MyBatis ...

  10. 自学WEB前端到什么程度才能就业

    做过多年web前端从业者,回答下这个问题 首先,这个问题主要问:自学web前端技术,如果才能找到一份web前端的工作.按照现在的招聘标准来看,无论你去哪个公司面试,你只需要满足他们公司的需求就可以. ...