python——哈希复习(构建哈希表&字典常规操作讲解)

小白的hash复习计划

先上传一下相关资源吧,直接丢百度网盘了。
一份字典的操作整理,一份md文档,和下面的相同
还有一份是python数据结构黑书。

准备是看一下python数据结构,黑书yyds

第十章讲的是哈希表,结构:

  1. 讲解了py中字典的一些行为,参见dic_use.py(下面第二个大标题)

  2. 用list装了一个键值对的类,实现了一个collision库的有序字典(其实就是没哈希的)

  3. 哈希知识复习

  4. 根据MapBase基类实现了一个自己的哈希表(建议自己跟着书上码一遍,线性探测这种的实现还是很有意思的)

知识点复习

原理:
其实还是使用一个桶数组(也就是一系列的空间),我们通过存储内容的特点,计算出一个数据,将数据作为下标放在桶数组的对应位置。
效果:
我们能保证查找、插入的时候是O(1)的,因为按照key算出来数据直接存储即可。
(可能会有一些冲突的情况,另说)
特点:
一般我们都会剩余一些空间,有一个装填因子的概念(存储空间/总空间),也是因为当剩余空间很少的时候,冲突的可能性是很高的。

哈希方式:
整数作为哈希值,注意py中的哈希码是32位的,可能需要处理。
多形式哈希码:x0* an-1+x1* an-2 ……,我们只需要选择一个合适的常量a(一般是一些素数,比如33、39这样的)
循环移位哈希码:桶形移位,其实效果也不错

同时因为哈希的原因,我们的key要求是不可变类型,如果是类的实例,需要实现hash魔法函数。

压缩函数:
在得到一个哈希码之后,我们实际上的一个桶数组是由大小的,不能直接使用。
(比如一个哈希码为32位1,但是整个哈希表只有十个格子,很明显不能按照下标来进行分配)

  1. 划分方式:
    说白了也就是映射,将i%N的值作为下标,N就是整个数组的大小
  2. MAD方式:
    说白了是一个划分方式的加强版:[(a*i+b)%p]%N
    使用一个质数p(p一般是大于总空间大小的一个质数),a、b则是[0,p)区间内的数据。
    其实也就是将之前的冲突再一次打乱。

冲突处理:
前面提过一嘴,有一个冲突的问题。
因为我们的哈希码实际上是做不到一个数据一个的,像身份证号那样的唯一,只能尽量保证没有重复(这也是判断一个哈希函数好不好的判断标准)

  1. 分离链表
    书上截个图吧,他这个图已经很形象了,就是冲突的节点就拉出来放置,都在同一个位置。
    (这里的markdown没办法,不让贴图,书上275页图10-6)
  2. 开放寻址
    我们可以理解成一个垃圾堆,另外开辟一个空间,我们每一次冲突就将冲突的内容分配到新的空间中,在查找的时候,如果计算出哈希码的位置不是想要的,就在垃圾桶里面寻找。
  3. 线性探测及其变种
    如果当前的位置被占据了,我们可以看一下旁边的位置,万一有不也是比较方便?
    比如当前位置5被相同哈希码的内容占据,我们根据线性探测+1,在6的位置查看,然后是7、8……
    那么在查找的时候,我们计算的位置和实际不相符,就需要在此位置上进行探测。

变种的话,二次探测就是使用+1,+4,+9这些平方数,或者是在冲突的基础上再使用一个哈希函数。

聚集问题:
如果是一个位置经常冲突,那么根据线性探测和二次探测,我们下一次填充就要比上一次填充远一个格,二次探测还好,线性直接全部堆在相邻的几个格子里面,很不方便。
二次哈希能解决这个问题。

py的实现方式还是很高端的,既然我们线性探测会有聚集问题,那么我们可以用随机数来决定偏移量啊。A[(h(k)+f(i))%N],其中f(i)为随机数。
这里肯定有人会问了,那随机数你后面怎么找? 别忘了,这里的随机数其实是伪随机数,当种子不变的时候,生成的数据是相同的。

字典相关操作

"""关于哈希表(字典的一些使用)"""
dic = {
    0:'',
    1:'x',
    2:'xy',
    3:'xyb',
    4:'xybb'
}

"""通过key访问:需要getitem魔法函数实现"""
print(dic[3])   
# 'xyb'

'''通过key替换:需要setitem实现
如果键不在字典中,就是添加'''
dic[3] = 'xyb '
print(dic[3])   
# 'xyb '

'''通过键删除:需要delitem实现
如果键不在字典中,报错keyerror'''
del dic[0]
print(dic)  
# {1: 'x', 2: 'xy', 3: 'xyb ', 4: 'xybb'}

'''返回字典的键值对个数,方法__len__实现'''
print(len(dic))
# 4

'''遍历,iter(dic)用的比较少,一般都是for key in dic,需要魔法函数__iter__'''
print(iter(dic))    
# <dict_keyiterator object at 0x0000024ED11BC130>,可迭代对象
for key in dic:
    print(key, dic[key])
# 1 x
# 2 xy
# 3 xyb
# 4 xybb

'''成员运算符in:判断一个键是否在dic中,返回Boolean型'''
if 5 in dic:
    print('yes')
else:
    print('no')
# no

'''get(k, d=None) k是键,d为查找失败的返回值,默认none并报错'''
print(dic.get(4))
# 'xybb'
print(dic.get(5))
# 这里按照书上说的是报错,但是亲测是返回一个none?
# 去查了一下,也没看到有说报错的,可能是书有问题
print(dic.get(5,None))
# 还是没报错
print(dic.get(5, 'what are you doing? f**k you!'))
# 返回d

'''dic.setdefault(k,d) 如果k in dic,返回dic[k],否则将dic[k]设置为d,并返回d'''
print(dic.setdefault(4,'xybb yyds'))
# 'xybb'
print(dic.setdefault(5, 'xybb yyds'))
# 'xybb yyds'

'''dic.pop(k, d=none) 弹出key=k的键值对,并返回对应的值,如果k不在,返回d,d=none报错'''
print(dic.pop(4))
# 'xybb'
print(dic.pop(6))
# 报错了
print(dic.pop(6,'yyds'))
# 'yyds'
print(dic)
# {1: 'x', 2: 'xy', 3: 'xyb ', 5: 'xybb yyds'},4没了

'''dic.popitem() 随机删除一对,返回一个元组,为空报错'''
print(dic.popitem())
# (5, 'xybb yyds')
print(dic)
# {1: 'x', 2: 'xy', 3: 'xyb '}, 5没了
print({}.popitem())
# 报错

'''dic.clear() 清空'''
dic.clear()
print(dic)
# {}

dic = {
    0:'',
    1:'x',
    2:'xy',
    3:'xyb',
    4:'xybb'
}

'''keys() values() items() 键、值、键值对的可迭代对象'''
for key in dic.keys():
    print(key)
# value item使用同理,不过items是返回一个元组,我们可以用解封装拆成k-v两个变量用
# 这部分是可迭代对象,一般来说打印了也不行

'''update,可以理解成列表的extend'''
dic.update({'1':1,'2':2})
print(dic)
# {0: '', 1: 'x', 2: 'xy', 3: 'xyb', 4: 'xybb', '1': 1, '2': 2}

'''也可以用==和!=判断相等'''
dic = {'xybb':'yyds'}
dic2 = {'xybb':'yyd'}
if dic==dic2:
    print('yes')
else:
    print('no')
# no
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值