python 数据结构 详解哈希表 哈希碰撞 hashSet与字典
哈希表 hashtable
hashfunc构造方法:
取余法,
平方留中法,平方后留中间的数再取余
非数项(字符串散列)# 转换为ascii码,分组,转换,相加求和再取余,记得因为字符串的特性,hashfunc还要加权重因子,不然‘cat’ ‘atc’会哈希碰撞
哈希碰撞解决方法:
开放定址
(线性探测 skip = 1)(跳跃探测 skip = 3 或质数)(再散列 skip = 1 ,3 ,5,7,9)
# 线性探测会导致数据聚集,同样设置散列表槽的长度时应选质数,这样会回避周期问题导致的分布不均匀
# 一般java和python设置的长度为 2^i -1
链定址
hash碰撞时,java会把重复的元素加到原来元素的指针下,数组指针实现,如果长度大于8 ,会用红黑树优化
#例 77 44在长度为11的散列表里,hashfunc后的索引都为0 ,此时链定址不会在原散列表上寻找空位,而是把77指向44
同理 当存入55时,索引也为0,此时44指向55
负载因子和完美散列函数
负载因子 负载因子𝛌 = 数据项个数 / 散列表的大小
给定一个数据项,如果一个散列函数能把每个数据项映射到不同的槽中,那么这个散列函数就可以称为“完美散列函数”。
# md5 和 sha 是著名的近似完美散列函数
MD5(Message Digest)将任何长度的数据变换为固定长为128位(16字节)的摘要。
SHA(Secure Hash Algorithm)是另一组散列函数。
SHA-0 / SHA-1 输出散列值160位(20字节)
SHA-256 / SHA-224 分别输出256位、224位
SHA-512 / SHA-384分别输出512位和384位
哈希的应用:
摘要算法 数据加密
数据验证 版权校验 大文件分块校验
负载均衡 一致性hash 服务器缩扩容 虚拟结点
hashSet实现字典
# python字典实现的内部hashfunc不是简单的取余,是更复杂的寻址模式
# hash 哈希 散列表 O(1) # ASCII 数值散列一个字符串 def hash(aString, tablesize): sum = 0 n = 0 for pos in range(len(aString)): n += 1 sum = sum + ord(aString[pos])*n return sum%tablesize # 未设置权重 会导致次序不同的字符串存放在同一个槽中 # 4 4 权重后 3 10 print(hash('cat', 11)) print(hash('atc', 11)) # 映射 hashSet python dict 简单字典的实现 class hashTable(): def __init__(self): self.size = 11 self.slots = [None]*self.size self.data = [None]*self.size def __iter__(self): for key in self.slots: if key: yield key def items(self): for key in self.slots: if key: yield (key, self.data[self.slots.index(key)]) def keys(self): for key in self.slots: if key: yield key def datas(self): for key in self.slots: if key: yield self.data[self.slots.index(key)] def clear(self): for key in self.slots: if key: self.data[self.slots.index(key)] = None self.slots.clear() def hashfunction(self,key,size): return key%size def rehash(self,oldhash,size): return (oldhash+1)%size def put(self, key, data): hashvalue = self.hashfunction(key, len(self.slots)) if self.slots[hashvalue] == None: # 依次填散列表。填入data self.slots[hashvalue] = key self.data[hashvalue] = data else: if self.slots[hashvalue] == key: # 再次put进已有key的键值对,data会被替换成最新的data self.data[hashvalue] = data else: # key散列表内既不为空未存上,也不相等,此时还能取到相同的hashvalue,即hash碰撞 nextslot = self.rehash(hashvalue,len(self.slots)) # 再散列函数 此时线性检测+1 while self.slots[nextslot] != None and self.slots[nextslot] != key: # 若再次碰撞,继续再散列 nextslot = self.rehash(nextslot, len(self.slots)) if self.slots[nextslot] == None: # 同上,填入值 self.slots[nextslot]=key self.data[nextslot]=data else: self.data[nextslot] = data # 同上,更换值 def gets(self,key): startslot = self.hashfunction(key,len(self.slots)) data = None stop = False found = False position = startslot while self.slots[position] != None and not found and not stop: if self.slots[position] == key: found = True data = self.data[position] else: position = self.rehash(position,len(self.slots)) if position == startslot: stop = True return data def __getitem__(self,key): return self.get(key) def __setitem__(self, key, data): return self.put(key, data) h = hashTable() h[54] = 'cat' h[26] = 'dog' h[20] = 'chicken' for x in h.keys(): print(x) for y in h.datas(): print(y) for item in h.items(): print(item)
运行结果:


浙公网安备 33010602011771号