python字典相关操作的复杂度

最近对大量数据做处理时,耗时较大,后来通过改进用了字典,一定程度上提高了效率。
字典与列表的不同之处在于,可以通过键而不是位置来访问字典中的项。字典上的get item和set item操作是O(1)。另一个重要的字典操作是contains操作,即检查一个键是否在字典中也是O(1)。下图总结了所有字典操作的效率。该图中提供的效率是针对平均性能的。在一些罕见的情况下,contains、get item和set item操作可能会退化为O(n)性能。
在这里插入图片描述
字典相关操作的代码:

# dictionary 类似Map哈希表
d = {'cat': 'cute', 'dog': 'furry'}
print(d['cat'])
print('cat' in d)
d['fish'] = 'wet'
print(d['fish'])
# print(d['monkey'])  # KeyError: 'monkey' not a key of d
print(d.get('monkey', 'N/A'))
print(d.get('fish', 'N/A'))
del d['fish']
print(d.get('fish', 'N/A'))

d = {'person': 2, 'cat': 4, 'spider': 8}
for animal in d:
    legs = d[animal]
    print('A %s has %d legs' % (animal, legs))
    
d = {'person': 2, 'cat': 4, 'spider': 8}
for animal, legs in d.items():
    print('A %s has %d legs' % (animal, legs))
    
nums = [0, 1, 2, 3, 4]
even_num_to_square = {x: x ** 2 for x in nums if x % 2 == 0}
print(even_num_to_square)

使用python字典来进行查找的话速度非常快,是O(1)级别,因为是用hash实现的。
哈希表也叫散列表,可理解为借助哈希函数对数组这种数据结构进行扩展,利用的是数组支持按照下标随机访问元素的特性,是存储 Key-Value 键值对的集合。参考文章:知乎:什么是哈希表?
存放记录的哈希表结构如图所示(数组下标index与[key,value]键值对):
在这里插入图片描述

关于哈希表的基本原理可简单理解为:当我们用字典当中的key去获取对应的value时,会将key转换为列表当中的index,通过index去准确快速地获取到value。这里面的关键在于哈希函数与冲突检测
key转化为index的函数就称为哈希函数,其中key既可以为数字,也可以为字符串,哈希函数通过计算模值或计算字符串哈希值得到对应的数组下标(存储地址)。可见,不需要遍历地址,而只通过一次计算即得到地址,从而方便取该地址存放的值,则在不冲突的理想情况下,时间复杂度为O(1)。总体来说,若设计好哈希函数减少冲突次数,则时间复杂度的平均性能为O(1)。
而在计算index的时候要注意冲突检测(例如多个key通过哈希函数后同时对应到同一index),这时候主要通过index与其存放的key进行处理(可参考如下代码)。根据冲突检测的处理方法可以将检测方法分为开放寻址法拉链法
我们可以通过如下数组模拟散列表的问题进行理解,该问题实现了加入元素与判断元素是否存在的功能。通过计算得到要查找的key所对应的数组的下标k,并进行冲突检测(若有冲突,则k++),从平均性能上来说,确实可以达到O(1)。这种冲突检测方法为开放寻址法,除此之外还有基于链表的拉链法。该问题中存放的是key,即仅存放不重复的元素,其实就类似于hashset。若加入value,每个数组位置存放一个键值对[key, value],其实就类似于hashtable,key通常用来冲突检测与快速查找,而value用于存储对应于key的值。而hashtable与dictionary是较为相近的,具体区别之后再具体调研。
数组模拟散列表问题链接如下:
AcWing 840. 模拟散列表
该问题C++代码:

#include <cstring>
#include <iostream>

using namespace std;
// N为模数,null为空位置的初始值
// N通常取大于范围的质数,取质数冲突的概率最小
const int N = 2e5 + 3, null = 0x3f3f3f3f;

int h[N];

int find(int x) // 哈希函数:通过key得到对应的数组下标索引值(k)
{
    int k = ((x % N) + N) % N; // key->index(k)
    while(h[k] != null && h[k] != x) // 冲突检测
    {
        k ++;
        if(k == N) k = 0;
    }
    return k; // 返回地址
}

int main()
{
    int n;
    scanf("%d", &n);
    
    // 数组初始化为无穷大,无穷大表示不存放值
    memset(h, 0x3f, sizeof h); 
      
    while(n --)
    {
        char op[2];
        int x;
        scanf("%s%d", op, &x);
        int k = find(x);
        if(*op == 'I') h[k] = x; // 插入元素
        else // 判断存在
        {
            if(h[k] != null) puts("Yes");
            else puts("No");
        }
    }
    
    return 0;
}

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值