04 | 字典、集合,你真的了解吗?
04 | 字典、集合,你真的了解吗?
讲述:冯永吉
时长09:45大小8.91M
字典和集合基础
字典和集合性能
字典和集合的工作原理
插入操作
查找操作
删除操作
总结
思考题
赞 58
提建议
精选留言(164)
- pyhhou2019-05-17思考题 1: 第一种方法更快,原因感觉上是和之前一样,就是不需要去调用相关的函数,而且像老师说的那样 {} 应该是关键字,内部会去直接调用底层C写好的代码 思考题 2: 用列表作为 Key 在这里是不被允许的,因为列表是一个动态变化的数据结构,字典当中的 key 要求是不可变的,原因也很好理解,key 首先是不重复的,如果 Key 是可以变化的话,那么随着 Key 的变化,这里就有可能就会有重复的 Key,那么这就和字典的定义相违背;如果把这里的列表换成之前我们讲过的元组是可以的,因为元组不可变展开
作者回复: 正解
共 8 条评论428 - 燕儿衔泥2019-05-171.直接{}的方式,更高效。可以使用dis分析其字节码 2.字典的键值,需要不可变,而列表是动态的,可变的。可以改为元组
作者回复: 使用dis分析其字节码很赞
共 9 条评论86 - 豊2019-05-18老师,你好!有几个让我困惑的地方想跟您确认一下,问题有点多,希望不吝赐教! 1. 为了提高哈希表的空间利用率,于是使用了Indices、Entries结构分开存储(index)和(hashcode、key、value),这里的index是否就是Entries列表的下标? 2、如果问题1成立,通过hash(key) & (PyDicMinSize - 1)计算出来的是否为Indices列表的下标? 3、如果问题2成立,PyDicMinSize是什么?为什么要使用hashcode与(PyDicMinSize - 1)做与运算,相比起直接用hashcode作为Indices列表的下标会有什么好处? 4、如果问题2成立,在往字典插入新元素的时候,通过hash(key) & mask计算出Indices的下标,如果Indices对应的元素位置值为None,则是否会将其值(index)修改为len(Entries),然后在Entries列表追加一行新的记录(hashcode,key,value)? 5、如果问题2成立,在往字典插入新元素的时候,通过hash(key) & mask计算出Indices的下标,如果Indices对应的元素位置已经有值,则会跟Entries表中对应位置的key进行hash比较及相等比较来决定是进行value的更新处理还是hash冲突处理?展开共 13 条评论40
- Raymond2020-01-01我来尝试解释一下新旧哈希表存储的区别: 旧哈希表存储示意图: entries = [ ['--', '--', '--'] [-230273521, 'dob', '1999-01-01'], ['--', '--', '--'], ['--', '--', '--'], [1231236123, 'name', 'mike'], ['--', '--', '--'], [9371539127, 'gender', 'male'] ] 旧的哈希表的寻址过程是这样的:通过key计算哈希值,然后再通过哈希值计算数组的索引,然后通过索引以O(1)时间的复杂度访问到entries里面存储的哈希值、key和value; 但是旧的哈希表存储结构有个问题,就是为了保证通过计算所得的索引能够正确的访问到地址,需要给entries分配连续的存储空间,这样一来,中间空闲的空间就太多了,造成了空间浪费。 新哈希表存储示意图: indices = [None, 1, None, None, 0, None, 2] entries = [ [1231236123, 'name', 'mike'], [-230273521, 'dob', '1999-01-01'], [9371539127, 'gender', 'male'] ] 再来看新的哈希表存储结构,其寻址方式是这样的:通过key计算哈希值,然后再通过哈希值计算索引,但是这个索引不是entries的索引了,而是新建的indices数组的索引,需要先通过计算出的这个索引在indices中寻址来取到entries中对应内容的索引,然后通过新获取到的索引值再去entries中寻址获取最终需要的内容。 新的哈希存储结构多加了一个转换数组来存储entries数组的索引,这使得entries数组中的条目可以紧密排列,其思想就是将整条数据内容空闲的空间转换为单个索引空闲的空间,确实很划算,而且每次查询也只是多了一次在indices中的O(1)复杂度的寻址,查询性能影响不大。 不知理解的是否正确,请老师指正。展开共 4 条评论35
- Python高效编程2019-05-17Python 3.7 以后插入有序变为字典的特性。构造新字典的方式: 1. double star >>> d1 = {'name': 'jason', 'age': 20, 'gender': 'male'} >>> d2 = {'hobby': 'swim', **d1} 2. update 函数: >>> d3 = {'hobby': 'swim'} >>> d3.update(d1) 我们可以看到这两种方式得到的字典是满足插入有序的: >>> d3 {'hobby': 'swim', 'name': 'jason', 'age': 20, 'gender': 'male'} 在我的电脑上 第一种方式的性能要好一些。 double star: 229 ns ± 22.8 ns per loop update: 337 ns ± 49.7 ns per loop展开共 6 条评论35
- 许山山2019-05-17>>> dis.dis(lambda : dict()) 1 0 LOAD_GLOBAL 0 (dict) 3 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 6 RETURN_VALUE >>> dis.dis(lambda : {}) 1 0 BUILD_MAP 0 3 RETURN_VALUE >>> %timeit dict() 133 ns ± 2.95 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) >>> %timeit {} 74.6 ns ± 3.07 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) {}, [], () 都比 dict(), list(), tuple() 初始化列表的性能要好,因为后者需要函数调用消耗了更多的时间。展开共 2 条评论32
- 随风の2019-05-17文中提到的新的哈希表结构有点不太明白 None 1 None None 0 None 2 是什么意思? index是索引的话 为什么中间会出现两个None
作者回复: 这只是一种表示。None表示indices这个array上对应的位置没有元素,index表示有元素,并且对应entries这个array index位置上的元素。你看那个具体的例子就能看懂了
26 - 星文友2019-05-29--+-------------------------------+ | 哈希值 (hash) 键 (key) 值 (value) --+-------------------------------+ 0 | hash0 key0 value0 --+-------------------------------+ 1 | hash1 key1 value1 --+-------------------------------+ 2 | hash2 key2 value2 --+-------------------------------+ . | ... __+_______________________________+ 第一种数据结构,如何可以o(1)的查找一个key? 没有索引啊 这篇文章感觉写的不好,例子没有讲透 稀疏一定浪费吗,里面没有值的话能占用多少空间 我理解耗费空间的应该是k v的存储吧展开
作者回复: 根据key计算hash值后直接就可以找到对应的value啊,所以是O(1),他并不是列表需要遍历,他是哈希表 稀疏肯定浪费空间啊,里面没有值也是会有一定的存储损耗的 你自己去看看市面上Python教材里讲字典集合的,有几个能讲到像我这样深入。
共 4 条评论14 - Hoo-Ah2019-05-171. 直接使用大括号更高效,避免了使用类生成实例其他不必要的操作; 2. 列表不可以作为key,因为列表是可变类型,可变类型不可hash。 问题:为什么在旧哈希表中元素会越来越稀?
作者回复: 你比较一下旧哈希表和新哈希表的存储结构就会发现,旧哈希表的空间利用率很低,一个位置要同时分配哈希值,键和值的空间,但是新哈希表把indices和entries分开后,空间利用率大大提高。 看文中的例子,这是旧哈希表存储示意图 entries = [ ['--', '--', '--'] [-230273521, 'dob', '1999-01-01'], ['--', '--', '--'], ['--', '--', '--'], [1231236123, 'name', 'mike'], ['--', '--', '--'], [9371539127, 'gender', 'male'] ] VS 新哈希表存储示意图: indices = [None, 1, None, None, 0, None, 2] entries = [ [1231236123, 'name', 'mike'], [-230273521, 'dob', '1999-01-01'], [9371539127, 'gender', 'male'] ] 你数一下两个版本中空着的元素的个数,就很清晰了。
共 7 条评论11 - 力维2019-11-14内容挺好的,但好像有个小错误:关于查找价格的例子,列表查找并没有用到双重循环吧?A是循环,B只是判断语句,不构成循环。
作者回复: 语句“if x in list”虽然表面上是条件语句,但是内部是循环,因为判断一个元素在不在列表里,必须得遍历一遍,时间复杂度是O(n)
共 3 条评论9 - Ben2019-06-201, 第一种更快, 反编译后, 第二种需要调用函数 >>> import dis >>> def f1(): return {'1':1} ... >>> def f2(): return dict({'1':1}) ... >>> dis.dis(f1) 1 0 LOAD_CONST 1 ('1') 2 LOAD_CONST 2 (1) 4 BUILD_MAP 1 6 RETURN_VALUE >>> dis.dis(f2) 1 0 LOAD_GLOBAL 0 (dict) 2 LOAD_CONST 1 ('1') 4 LOAD_CONST 2 (1) 6 BUILD_MAP 1 8 CALL_FUNCTION 1 10 RETURN_VALUE 2. 不可以, 列表是可变的, 但是对hash表来说, 如果键值是可变的, 那么插入以及删除的位置都变成不确定的. 另外哈希冲突的概率也大大增加展开8
- 许山山2019-05-17老师我明白了,(hash, key, val) 都是存在 entries 里面的,通过 indices[index] 找到 entry 再做比较就好了。8
- farFlight2019-05-17老师好,在王争老师的数据结构课程中提到哈希表常与链表一起使用,譬如用来解决哈希冲突。请问python底层对字典和集合的实现是否也是这样的呢?
作者回复: 这个就是文中所说的线性寻找了,但是Python底层解决哈希冲突还有更好的方法,线性寻找是最简单的,但是不是最高效的
7 - Jon徐2019-05-17list indices就是哈希表,None表示该位置目前尚未被占用,索引的值即是在list entries中存储dict键值和哈希值的下标。 作业中初始化dict,key不能使用可变类型吧,value可以使任意对象。
作者回复: 回答的很好
6 - 刘朋2019-05-17插入操作, mask = PyDicMinSize -1 index = hash(key) & mask 能否有个例子,想详细了解一下细节共 3 条评论4
- 天凉好个秋2019-05-17不难想象,随着哈希表的扩张,它会变得越来越稀疏。 后面例子中解释的原因没看懂,能详细说说吗?
作者回复: 哈希表为了保证其操作的有效性(查找,添加,删除等等),都会overallocate(保留至少1/3的剩余空间),但是很多空间其实都没有被利用,因此很稀疏
共 2 条评论4 - 鱼腐2019-05-17Indices:none | one | none | index | none | index 是什么意思?能补充讲解下吗
作者回复: 这只是一种表示。None表示indices这个array上对应的位置没有元素,index表示有元素,并且对应entries这个array index位置上的元素。你看那个具体的例子就能看懂了
4 - Johnson2021-03-19indices = [None, 1, None, None, 0, None, 2] entries = [ [1231236123, 'name', 'mike'], [-230273521, 'dob', '1999-01-01'], [9371539127, 'gender', 'male'] ] 我的问题主要还是怎么查找到entries实际的值,以及indices是如何与entries关联起来的? 经过下面同学提供的帖子https://zhuanlan.zhihu.com/p/73426505 基本可以浓缩称这样的代码 entries[indices[hash(key) % mask]]展开3
- William2019-05-24老师请问,key、hash值、indice三者的联系是啥? 一直以为hash(key)就是内存地址3
- 庄小P2019-05-18entries = [ ['--', '--', '--'] [-230273521, 'dob', '1999-01-01'], ['--', '--', '--'], ['--', '--', '--'], [1231236123, 'name', 'mike'], ['--', '--', '--'], [9371539127, 'gender', 'male'] ] 为什么有些地方是一个['--', '--', '--'],有些地方是两个['--', '--', '--']呢?这里不好理解呀展开共 2 条评论3