当前位置: 首页 > 编程日记 > 正文

dmol3给定关键字不在字典中_python中的数据结构与算法(2):字典与集合

8d3026b697724dc5ed21d8a80d2370e9.png

1. 字典是什么

字典是便于信息检索的一种数据结构,鉴于信息检索在程序中无处不在,字典的使用场景也非常广泛,包括许多 python 内部机制的实现,也依赖字典结构,比如命名空间的管理等。

检索一般是根据关键字查找与它关联的信息,这些关键字应该是固定的,否则就会使原来保存的数据失效,也就是说,关键字必须是不可变对象。当然,在具体实现上,我们知道,python 是根据是否存在 __hash__ 方法来判断一个对象是否可以作为关键字的,用户可以自行实现这个方法,但也承担相应的风险。

对于信息检索来说,效率往往是关键考虑因素。如果是一个静态字典,主要考虑的是创建和查找的效率问题,而对于更常用的动态字典来说,在其中插入和删除数据的效率也很重要。

下面,我们从使用者的角度看一下字典所需要支持的操作,然后简单讨论字典的实现技术。当然,它需要支持的操作决定了它应该采用的实现技术。


从使用者的角度,我可以梳理一下字典需要支持的操作。

1.1 创建字典

python 字典提供了多种创建方式:

# 1. 直接使用大括号a = {'one': 1, 'two': 2, 'three': 3}# 2. dict(**kwargs) 方式,只能使用有效的 Python 标识符作为键a = dict(one=1, two=2, three=3)# 3. dict(mapping, **kwargs) 方式a = dict({'three': 3, 'one': 1, 'two': 2})# 4. dict(iterable, **kwargs) 方式a = dict([('two', 2), ('one', 1), ('three', 3)])a = dict(zip(['one', 'two', 'three'], [1, 2, 3]))# 5. 使用类方法 fromkeys(iterable[, value]),只能指定固定值,默认为Nonea = dict.fromkeys(['one', 'two', 'three'])# 6. 使用字典推导式a = {k:v for k,v in zip(['one', 'two', 'three'], [1, 2, 3])}

1.2 检查字典对象

# 1. 检查项数length = len(a_dict)# 2. 判断键是否存在if key in a_dict: passif key not in a_dict: pass

1.3 字典取值

# 使用键取值,如果键不存在,将抛出 KeyError 异常value = a_dict[key]# 使用 get 方法不会抛出 KeyError 异常,键不存在且没有指定默认值时,返回 Nonevalue = a_dict.get(key)

1.4 修改字典内容

# 插入或修改键值a_dict[key] = value# 使用 setdefault 方法时,如果键已存在,会返回其值,不会修改a_dict.setdefault(key, default_value)# pop方法会删除该项并返回value = a_dict.pop(key)  # 返回值item = a_dict.popitem()  # 以元组的方式返回键值对# 删除字典项,不存在时抛出 KeyErrordel a_dict[key]# 移除所有字典项a_dict.clear()# 字典间操作b_dict = a_dict.copy()a_dict.update(b_dict)

1.5 遍历

在处理遍历时,python 提供了一个字典视图对象,这种对象提供的是一个动态视图。也就是说,如果字典的内容发生了变化,视图也会随之变化:

# 以下方法提供的是字典视图对象a_dict.keys()a_dict.values()a_dict.items()iter(a_dict)  # 等价于 iter(a_dict.keys())# 视图对象是动态的>>> a = {'one': 1}>>> keys = a.keys()>>> keysdict_keys(['one'])>>> a['two'] = 2>>> keysdict_keys(['one', 'two'])  # 内容发生了变化

2. 字典的实现技术

2.1 基于线性表

显然,我们可以用列表来保存字典的键和关联的数据对象。

使用普通线性表时,数据检索的效率是 O(n) ,因为必须一一比对,直到找到键值,或者确定表中不存在该键值。如果我们改进结构,采用有序表,那么,通过二分法,可以以 O(logn) 的效率找到数据,是比较快的。不过,数据并不一定都可以排序,在实践中,甚至需要使用不同类型的对象作为字典的键,这时使用有序表就会遇到一些障碍。

而对数据插入和删除来说,基于线性表实现的字典在效率上是不能让人满意的。如果采用普通线性表,添加数据的时间复杂度是 O(1),删除数据则为 O(n);如果采用有序表,则插入和删除数据都是 O(n) 的时间复杂度。

线性表的另一个问题是需要连续的存储空间,对于规模较大的字典来说,也会造成一些困难。

总的来说,对于频繁变动,需要支持众多类型的键,或者规模较大的字典来说,线性表可能不是最好的选择。


2.2 基于树形结构

树形结构是检索系统的常用数据结构。它不需要连续的存储空间,可以支持大规模的数据,如果设计科学,也可以实现高效的检索和修改。

理论上说,普通二叉树可以实现平均意义上的 O(logn) 检索效率和修改效率。但这种效率是没有保证的,如果没有及时控制和调整,普通二叉树很有可能在持续的数据变动过程中发生结构退化,某个分支无限延伸,导致检索和操作效率往 O(n) 靠近。

解决结构退化问题的思路,是在数据变动过程中,不断调整其结构,使其保持相对平衡。因此,在实践中可以采用平衡二叉树,或者红黑树,从而实现稳定的 O(logn) 的检索和操作效率。

如我们所知,除了二叉树,实践中也常常使用多分支排序树,或者说 N叉树。考虑到从磁盘读取数据时,其实是按块读取的,在一个节点上存储更多数据有利于减少磁盘读写操作,因此,多分支排序树在数据库系统中得到很好的应用。比如 MySQL 的 InNoDB 引擎,采用 B+ 树作为索引结构,如果以整数字段作为索引,每个节点大约可以有 1200 个分支。

当然,python 中的字典和集合采用的并不是基于树形结构的实现,而是基于哈希表的实现,在理想情况下,可以实现 O(1) 复杂度的检索和修改。


2.3 基于哈希表

哈希表也叫散列表,本质上是一个线性表。线性表一定有一个下标区间,比如说一个可以存放 8 个元素的列表,其下标区间就是 0-7,如果我们可以设计一种函数,把字典的键映射为这个区间内的一个整数,就可以直接根据键计算得到它存储的具体位置,因而就可以实现快速访问和修改了,这个函数就是散列函数,或者哈希函数。

散列函数的设计有很多专业的研究,总的来说,它需要实现的基本性质包括:

  • 对于同一个对象,应该得到同样的散列结果;
  • 散列得到的结果范围应该尽量覆盖整个下标空间,否则就会造成空间浪费;
  • 不同键的散列结果应该在整个下标空间均匀分布,否则就会出现严重的冲突问题;
  • 函数的计算最好比较简单,从而减少计算开销;

容易发现,采用散列表技术实现字典,会有两个绕不过去的问题:

  • 第一个问题是,空间是有限的,也就是说,和列表一样,随着字典内容的增长,一定会出现空间扩展的问题;
  • 第二个问题是,由于空间是有限的,不论我们采用哪种散列技术,总是会碰到不同的键,散列结果为同一个整数的情况,也就是说,出现冲突;

对于第一个问题,python 字典采用的空间扩展策略和列表的空间扩展策略类似。当我们创建一个空字典或者很小的字典时,初始分配的存储区可以容纳 8 个元素,当存储区的使用超过 2/3 时,字典会更换更大的存储区,并把之前保存的内容重新散列到新存储里。当元素个数低于 50000 个时,新扩展的存储区是原存储区的 4 倍,超过之后则调整为 2 倍。

为什么元素个数超过 2/3 就进行扩展呢?这个问题涉及到散列表的冲突处理技术。简单地说,由于 python 采用的是内部消解技术,为保持散列表的平均检索速度接近常数,需要控制其负载因子,也就是元素占空间可存储元素数量的比重低于 0.7 到 0.75。


3. 冲突处理

哈希表一定会有哈希值冲突问题。比如说,一个可以存放 8 个元素的表,不同键计算得到的哈希值都是 5,这时该怎么处理呢?

一种解决方案是使用外部空间,叫外部消解技术。比如说,对于所有出现冲突的键值,可以统一放到外部的某个列表,检索的时候,如果发现地址为 5 的空间存储的不是需要的键,就到外部列表查找,如果外部列表也没有找到,就判定这个键不存在。或者说,我们在哈希表中不直接保存对象(或者对象的引用),而保存一个列表的引用,所有哈希值为 5 的对象(或其引用)都保存在这个列表里等。具体实现可以采用多种不同的策略。

另一种解决方案是内部消解技术,也就是说,如果出现冲突,后进来的值依然会保存在内部空间,只是存储的位置做了调整。具体的调整策略可以多种多样,比如说,直接使用下一个空地址,或者再次进行散列操作等。这样,检索键值的时候,如果原有位置存储着其它键,就按规则继续检索,直到找到,或者遇到一个未使用的空间,从而判断该键不存在。

python 字典采用的是内消解技术。

不论如何,哈希值冲突都是我们希望尽量避免的,最坏情况下,所有键值都冲突,哈希表就会退化为一个线性表,数据检索复杂度会从 O(1) 上升为 O(n)。

事实上,由于哈希表必然出现冲突,因此在安全领域,有哈希碰撞攻击的思路。通过精心构造的、哈希值一致的数据,对同一个 API 发起请求,导致服务器 CPU 都消耗在数据检索上,无法快速响应请求,从而实现 DOS 的目的。


4. 进阶字典对象

在 python 中,除了内置的字典,collections 模块还提供了另外两种常用的字典: defautdict 和 OrderDict。

defaultdict 实际上只是扩展了字典的 __missing__ 方法,支持用户提供一个 default_factory 函数,用于默认值的生成。当我们从字典取值时,如果字典没有对应的键,就会调用 __missing__ 方法,如果未定义这个方法,则直接返回 KeyError 。值得注意的是,__missing__ 方法不会被 __getitem__ 以外的方法调用,也就是说,如果我们使用 get() 函数取值,依然会返回指定的默认值或 None。

和常规字典相比,OrderDict 对象内部维护着一个根据键插入顺序排序的双向链表,新插入的元素会被放到链表的尾部,从而实现记住插入顺序的功能。不过,python3.7 版本之后,内置字典已经实现了一样的能力,并在 python3.8 版本提供了 __reversed__() 方法,因此,OrderDict 已经没什么存在的必要了。

进一步了解可以参考官方文档。


5. 集合

python 提供了两种内置集合类型:set 和 frozenset。它们的实现技术与字典是一致的,当然,frozenset 是不可变对象,其实现不需要考虑空间扩展的问题。

fronzenset 与 set 的关系有点类似于 tuple 与 list 的关系,形式上分为不可变和可变,底层技术基本一致,在实践中却往往用于完全不同的场景。

和字典不一样的地方在于,集合需要考虑数学中的集合类计算,如交集、并集、差集与对称差集等等。如果有兴趣,也可以直接查看官方文档。


END

相关文章:

HTTP项目1.0 -- HTTP协议基础知识

一. HTTP之URL篇首先来看一下,我们一般在上网的时候,地址栏中经常会显示的信息,这里就举一些简单的例子https://www.baidu.comhttps://113.2.7.58.25/a/b/c.html从上面的简单的例子我们把url分成了以后的几个部分,请看下图第一个协…

SQL Server 远程无法连接

1. 查看默认1433端口是否已经开启。转载于:https://www.cnblogs.com/jiajinyi/archive/2013/05/21/3091091.html

WCF客户端不能用在Using语句块中,因为它可能会抛出不可预知的异常。即使你捕获了异常,仍有可能一直保持连接。...

WCF客户端不能用在Using语句块中,因为它可能会抛出不可预知的异常。即使你捕获了异常,仍有可能一直保持连接。让我们来看看形成这一问题的历史原因,并提出几个补救措施。 在.NET中,资源管理的基础就是IDisposable和Using语句块。除…

关于 MongoDB 与 SQL Server 通过本身自带工具实现数据快速迁移 及 注意事项 的探究...

背景介绍 随着业务的发展、需求的变化,促使我们追求使用不同类型的数据库,充分发挥其各自特性。如果决定采用新类型的数据库,就需要将既有的数据迁移到新的数据库中。在这类需求中,将SQL Server中的数据导入到MongoDB 中显得尤为突…

语音计算矩形面积_LeetCode85-最大矩形

今天在制作书签的时候突然想到了一个问题如果要送给未来的女朋友一个书签上面该写些什么话哈哈哈哈哈哈哈哈哈The Spring is coming!想了一会儿,觉得这句话最合适To xxx:天使的笑,灿烂的心!!!哎,还是先找个…

模板的分离编译

模板不支持分离编译我们来分析一下模板为什么不支持分离编译呢&#xff0c;所谓的分离编译就是我们在编写程序的时候可能会出现如下的一种情况就是&#xff0c;&#xff08;我下面就是举具体的例子了&#xff09;代码//*****************template.h***********// #include<i…

什么是壳 - 脱壳篇01

什么是壳 - 脱壳篇01 让编程改变世界 Change the world by program 壳 在自然界中&#xff0c;植物用壳来保护种子&#xff0c;动物用壳来保护身体&#xff0c;我们人类没有壳&#xff0c;但我们有衣服&#xff0c;房子也起到了壳的作用。不仅保护&#xff0c;而且美观。 同…

push、pop指令

push、pop指令转载于:https://www.cnblogs.com/LoveFishC/archive/2012/07/25/3846605.html

个人前端学习路线图与github优秀前端开发者的路线图推荐

1、个人目前学习的路线图 2、github优秀前端开发者的路线图推荐 打开github首页&#xff0c;在搜索框输入developer-roadmap&#xff0c;搜索github前端路线图 选择kamranahmedse/developer-roadmap拥有56.5k的星&#xff0c;足以证明这个路线受到广大前端开发者的喜爱与推荐 选…

智能指针1.0

一.使用普通的动态内存开辟存在的问题 我们在使用动态内存开辟一个空间的时候&#xff0c;需要释放掉这个空间&#xff0c;不然就容易出现内存泄漏。 比如下面的程序 情况一&#xff1a; #include<iostream> using namespace std; int errorTest() { intflag 0; …

gen_event中的handler和supervised handler

呃&#xff0c;在gen_event中有两个添加handler的方法 gen_event:add_handler/3 gen_event:add_sup_handler/3 一开始总是有些迷惑两者的区别&#xff0c;今天查看了gen_event源码&#xff0c;总算弄清两者的区别。 add_handler添加的只是把gen_event作为容器&#xff0c;仅仅在…

动态刷新_屋盖“起飞”刷新国内记录,中建八局杭州萧山国际机场项目最新动态来袭...

近日&#xff0c;中建八局承建的杭州萧山国际机场三期项目完成了一件“壮举”T4航站楼首段钢屋盖网架顺利提升至设计标高一举刷新了国内机场航站屋盖单次提升的记录正式进入主楼屋面及幕墙施工的新篇章两段视频速看首段钢屋架提升刷新记录 覆盖测量全过程监控杭州萧山国际机场…

逻辑 STANDBY ORA-00368日志应用失败处理一例

故障现象&#xff1a;逻辑STANDBY数据库注册日志成功&#xff0c;但应用日志出现错误&#xff0c;提示“ORA-00368: checksum error in redo log block”&#xff0c;显然是文件受到了破坏。Tue Jul 24 08:25:59 2012LOGMINER: WARNING: error 368 encountered, failed to read…

Linux 下实现虚拟光驱功能,查看iso文件内容

1,创建挂载点&#xff08;也可以不创建&#xff0c;直接用现有的目录&#xff09;openSUSE:~ # mkdir /mnt/iso2&#xff0c;挂载ISO文件至创建的挂载点openSUSE:~ # mount -t iso9660 -o loop /home/ubuntu-14.04.5-server-amd64.iso /mnt/isomount参数解释&#xff1a;-t&…

clientcontainerThrift Types

首先声明&#xff0c;我是一个菜鸟。一下文章中出现技术误导情况盖不负责 来自Apache Thrift官网&#xff1a;Thrift Types Thrift Types The Thrift type system is intended to allow programmers to use native types as much as possible, no matter what programming lang…

简易git操作 -- 让你的格子绿起来

创建github账号 浏览器输入网址&#xff0c;申请一个github账号&#xff0c;github申请网址&#xff0c;看到下面的图片内容&#xff0c;点击图中红色框里面的内容&#xff0c;用邮箱账号申请一个github账号&#xff0c;一定记住账号和密码 填写注册信息 点击之后跳转到下面…

c语言自定义char*函数返回值是乱码_[每日C语言」printf()函数的修饰符和返回值...

在上一个小demo《printf()函数(1)》中主要说了一下printf()函数的转换说明符&#xff0c;这些转移说明符是可以被修饰的。我们可以在%d和定义的转义字符之间通过插入修饰符对基本的转换说明加以修改。printf()修饰符digit(s) 字符宽度的最小值结果&#xff1a;不够的前面补空格…

win2003辅助域服务器相关几个错误日志的解决办法

1.域助域上做了DNS后,提示:浏览器无法更新服务状态位,数据有错误,错误代码是8007关闭computer browser基本就行了,有人说还要关server,它负责共享之类的,如果关了,就不能共享了,我个人没有关!2.之前,公司主域上有DNS,不过没有允许复制区域,也没有在辅助域上做DNS,所以在辅助域上…

redis.conf配置文件参数说明

参数说明 redis.conf 配置项说明如下&#xff1a;1. Redis默认不是以守护进程的方式运行&#xff0c;可以通过该配置项修改&#xff0c;使用yes启用守护进程daemonize no2. 当Redis以守护进程方式运行时&#xff0c;Redis默认会把pid写入/var/run/redis.pid文件&#xff0c;可以…

用C#来开发CAD插件,含源代

CAD插件看起来很神秘&#xff0c;其实一个合格码农经过几天就能快速掌握。没什么秘密&#xff0c;开发CAD插件和winform一样简单&#xff0c;多学几个类库用法就是&#xff0c;在CAD里展现界面和winform略有不同。学习CAD插件开发的动机是为了薪水&#xff0c;由于公司是做显示…

动态内存管理和智能指针 2.0 -- shared_ptr

shared_ptr出现原因 通过第一章的学习&#xff0c;我们知道不管是auto_ptr合适scoped_ptr都是存在缺陷的&#xff0c;于是我们必须想出一个方法既能很好的管理我们的内存&#xff0c;而且在使用的时候&#xff0c;可以多个指针指向一个内存&#xff0c;这个时候就出现了shared…

汇总同一时间段的数据_数据集干货:一文读懂Mapsidejoin

我们知道数据分析的第一步是准备数据&#xff0c;所以在前面的课程里&#xff0c;我们介绍了元数据。今天这篇文章&#xff0c;主要介绍大数据量组合数据集在永洪中的应用实例&#xff1a;Mapsidejoin。什么是Mapsidejoin&#xff1f;按照字面意思&#xff0c;Mapsidejoin就是M…

【强烈推荐】国土档案管理信息系统产品使用说明书系列目录【附下载地址】...

<<国土档案管理信息系统>>产品使用说明书系列目录【附下载地址】——通过知识共享树立个人品牌。《国土档案管理信息系统》在线视频讲解一、记大型商业软件<<国土档案管理信息系统>>之系统简介记大型商业软件 > 之系统简介 ——通过知识共享树立个人…

zip函数的使用

s [[1, 10], [1.2, 11], [2, 5], [5, 15]] data zip(*s) x_list data[0] y_list data[1] x_min min(x_list) x_max max(x_list) y_min min(y_list) y_max max(y_list) box [x_min, x_max, y_min, y_max] print(box) # [1, 5, 5, 15] 转载于:https://www.cnblogs.com/…

计算机网络基础 1.0 -- 概述

概念理解 报文&#xff1a;在网络中发送的数据块成为报文在发送报文之前&#xff0c;通常会把数组分组&#xff0c;每个组都有个包头和数据组成&#xff0c;包头中包含了诸如目标地址和源地址等重要信息&#xff0c;这样才保证了数据能够有目的的在网络中的传输主机是用户用来…

字符串循环同构的最小表示法(转)

循环字符串的最小表示法的问题可以这样描述&#xff1a; 对于一个字符串S&#xff0c;求S的循环的同构字符串S’中字典序最小的一个。 由于语言能力有限&#xff0c;还是用实际例子来解释比较容易&#xff1a;设Sbcad&#xff0c;且S’是S的循环同构的串。S’可以是bcad或者cad…

周长相等的正方形面积一定相等_必考单元:三年级下册面积计算公式+知识点+测试卷(附答案),重点内容,收藏练习!...

《面积》公式 知识点面积和面积单位&#xff1a;1.常用的面积单位有&#xff1a;(平方厘米)、(平方分米)、(平方米)。2.理解面积的意义和面积单位的意义。面积&#xff1a;物体表面或封闭图形的大小&#xff0c;叫做它们的面积。1平方米&#xff1a;边长是1米的正方形&#xff…

sql server 2000 版本查询

确定已安装的 SQL Server 2000 Database Components 版本 使用 isql、osql 或查询分析器&#xff0c;对数据库引擎实例执行以下查询之一。 SELECT SERVERPROPERTY(ProductLevel) SELECT VERSION SELECT SERVERPROPERTY(Produc…

ubuntu16创建开机启动服务

1、cd /etc/init.d/ 2、sudo touch zookeeper&#xff08;举例&#xff09; 3、给服务赋权限&#xff1a;sudo chmod x zookeeper 4、执行sudo vim zookeeper 命令写入执行脚本&#xff08;启动脚本中的启动命令对应服务的启动命令&#xff09; #! /bin/sh### BEGIN INIT INFO …

Effective C++ 1.0 -- 概述

声明 对象声明&#xff0c;函数声明&#xff0c;类型声明&#xff0c;是告诉编译器某个东西的 名称和类型&#xff0c;但是略去了实现 细节&#xff0c;因为定义在其他的地方。 external int x; //对象&#xff08;object&#xff09;声明 std:size_t numDigits(int num…