开发者

关于Python字典的底层实现原理

目录
  • 字典是否是有序
  • 字典的查询、添加、删除的时间复杂度
  • 字典的实现原理
    • python3.6之前的无序字典
    • 带入具体的数值来介绍
    • Python3.7+后的新的实现方式
    • Python3.7+带入数据演示
    • 查询字典
  • 时间复杂度
    • 常见的哈希冲突解决方法
      • 总结

        字典是否是有序

        在Python3.6之前,字典是无序的,但是Python3.7+,字典是有序的。

        在3.6中,字典有序是一个implementation detail,在3.7才正式成为语言特性,因此3.6中无法确保100%有序。

        字典的查询、添加、删除的时间复杂度

        字典的查询、添加、删除的平均时间复杂度都是O(1),相比列表与元祖,性能更优。

        字典的实现原理

        Python3.6之前的无序字典

        字典底层是维护一张哈希表,可以把哈希表看成一个列表,哈希表中的每一个元素又存储了哈希值(hash)、键(key)、值(value)3个元素。

        关于Python字典的底层实现原理

        enteies = [
            ['--', '--', '--'],
            [hash, key, value],
            ['--', '--', '--'],
            ['--', '--', '--'],
            [hash, key, value],
        ]
        

        带入具体的数值来介绍

        # 给字典添加一个值,key为hello,value为word
        # my_dict['hello'] = 'word'
        
        # hash表初始如下
        enteies = [
            ['--', '--', '--'],
            ['--', '--', '--'],
            ['--', '--', '--'],
            ['--', '--', '--'],
            ['--'开发者_JS学习, '--', '--'],
        ]
        
        hash_value = hash('hello')  # 假设值为 12343543 
        
        index = hash_value & ( len(enteies) - 1)  # 假设index值计算后等于3
        
        # 下面会将值存在enteies中
        enteies = [
            ['--', '--', '--'],
            ['--', '--', '--'],
            ['--', '--', php'--'],
            [12343543, 'hello', 'word'],  # index=3
            ['--', '--', '--'],
        ]
        
        # 继续向编程客栈字典中添加值
        # my_dict['color'] = 'green'
        
        hash_value = hash('color')  # 假设值为 同样为12343543
        index = hash_value & ( len(enteies) - 1)  # 假设index值计算后同样等于3
        
        # 下面会将值存在enteies中
        enteies = [
            ['--', '--', '--'],
            ['--', '--', '--'],
            ['--', '--', '--'],
            [12343543, 'hello', 'word'],  # 由于index=3的位置已经被占用,且key不一样,所以判定为hash冲突,继续向下寻找
            [12343543, 'color', 'green'],  # 找到空余位置,则保存
        ]
        

        enteies表是稀疏的,随着我们插入的值不同,enteies表会越来越稀疏(enteies也是一个会动态扩展长度的,每一此扩展长度,都会重新计算所有key的hash值),所以新的字典实现就随之出现。

        Python3.7+后的新的实现方式

        关于Python字典的底层实现原理

        Python3.7+带入数据演示

        # 给字典添加一个值,key为hello,value为word
        # my_dict['hello'] =编程 'word'
        
        # 假设是一个空列表,hash表初始如下
        indices = [None, None, None, None, None, None]
        enteies = []
        
        hash_value = hash('hello')  # 假设值为 12343543
        index = hash_value & ( len(indices) - 1)  # 假设index值计算后等于3
        
        # 会找到indices的index为3的位置
        indices = [None, None, None, 0, None, None]
        # 此时enteies会插入第一个元素
        enteies = [
            [12343543, 'hello', 'word']
        ]
        
        # 我们继续向字典中添加值
        my_dict['haimeimei'] = 'lihua'
        
        hash_value = hash('haimeimei')  # 假设值为 34323545
        index = hash_value & ( len(indices) - 1)  # 假设index值计算后等于 0
        
        # 会找到indices的index为0的位置
        indices = [1, None, None, 0, None, None]
        # 此时enteies会插入第一个元素
        enteies = [
            [12343543, 'hello', 'word'],
            [34323545, 'haimeimei', 'lihua']
        ]
        

        查询字典

        # 下面是一个字典与字典的存储
        more_dict = {'name': '张三', 'sex': '男', 'age': 10, 'birth': '2019-01-01'}
        
        # 数据实际存储
        indices = [None, 2, None, 0, None, None, 1, None, 3]
        enteies = [
            [34353243, 'name', '张三'],
            [34354545, 'sex', '男'],
            [23343199, 'age', 10],
            [00956542, 'birth', '2019-01-01'],
        ]
        
        print(morphpe_dict['age'])  # 当我们执行这句时
        
        hash_value = hash('age')  # 假设值为 23343199
        index = hash_value & ( len(indices) - 1)  # index = 1
        
        entey_index = indices[1]  # 数据在enteies的位置是2
        value = enteies[entey_index]  # 所以找到值为 enteies[2]
        

        时间复杂度

        字典的平均时间复杂度是O(1),因为字典是通过哈希算法来实现的,哈希算法不可避免的问题就是hash冲突,Python字典发生哈希冲突时,会向下寻找空余位置,直到找到位置。

        如果在计算key的hash值时,如果一直找不到空余位置,则字典的时间复杂度就变成了O(n)了。

        常见的哈希冲突解决方法

        1 开放寻址法(open addressing)

        开放寻址法中,所有的元素都存放在散列表里,当产生哈希冲突时,通过一个探测函数计算出下一个候选位置,如果下一个获选位置还是有冲突,那么不断通过探测函数往下找,直到找个一个空槽来存放待插入元素。

        2 再哈希法

        这个方法是按顺序规定多个哈希函数,每次查询的时候按顺序调用哈希函数,调用到第一个为空的时候返回不存在,调用到此键的时候返回其值。

        3 链地址法

        将所有关键字哈希值相同的记录都存在同一线性链表中,这样不需要占用其他的哈希地址,相同的哈希值在一条链表android上,按顺序遍历就可以找到。

        4 公共溢出区

        其基本思想是:所有关键字和基本表中关键字为相同哈希值的记录,不管他们由哈希函数得到的哈希地址是什么,一旦发生冲突,都填入溢出表。

        总结

        以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

        0

        上一篇:

        下一篇:

        精彩评论

        暂无评论...
        验证码 换一张
        取 消

        最新开发

        开发排行榜