pygorithm/hash_map/hash_map.py

83 lines
3.0 KiB
Python
Raw Permalink Normal View History

2023-12-11 18:17:16 +08:00
"""
hash map 是将数据项映射到列表中的对应位置由于有这样的映射关系
可以使用 hash map 实现哈希查找查找复杂度为 O(1)
计算映射的方式就是 hash 函数一般采用对列表长度取余的方式
hash 表重要指标负载表示已被占用的空间/总空间该比值也被称谓负载因子
当负载因子太大时需要扩容
hash 函数例如使用项值直接对一个数取余会存在几个项取余的结果相同这样就带来了冲突
也就是一个槽对应了多个值
冲突解决
1. 最简单的方法是从原哈希冲突处开始以顺序方式移动槽直到遇到第一个空槽
遇到末尾后可以循环从头开始查找这种冲突解决方法被称为开放寻址法线性查找的缺点是数据项聚集
2. 处理数据项聚集的一种方式是扩展开放寻址技术发生冲突时不是顺序查找下一个开放
而是跳过若干个槽从而更均匀地分散引起冲突的项比如每次隔三个槽来查看
在冲突后寻找另一个槽的过程叫重哈希重散列, rehash
其计算方法如下rehash(pos) = (pos + n)%size
要注意跳过的大小必须使得表中的所有槽最终都能被访问为确保这一点建议表大
小是素数这也为什么示例中要使用 11
3. 解决冲突的另一种方法是拉链法也就是说对每个冲突的位置我们设置一个链表来保
存数据项如图5.5
- 查找时发现冲突后就再到链上顺序查找复杂度为 O(n)当然
冲突链上的数据可以排序然后再借助二分查找这样哈希表复杂度为 O(log2(n))
- 拉链法是许多编程语言内置的哈希表数据结构解决冲突的默认实现
4. 如果采用扩容来解决冲突需要将原来表中的键值对重新计算
"""
class HashMap:
def __init__(self, size: int):
self.size = size
self.slot_used = 0
self.data: list[int | None] = [None for _ in range(self.size)]
self.slot: list[int | None] = [None for _ in range(self.size)]
def hash(self, key: int) -> int:
return key % self.size
def rehash(self, key: int) -> int:
return (key + 3) % self.size
def load(self):
return self.slot_used / self.size
def expand(self):
self.slot += [None for _ in range(self.size)]
self.data += [None for _ in range(self.size)]
self.size *= 2
def insert(self, key: int, value: int):
if self.load() >= 0.75:
self.expand()
pos = self.hash(key)
if not self.slot[pos]:
self.slot[pos] = key
self.data[pos] = value
else:
while self.slot[pos]:
pos = self.rehash(pos)
self.slot[pos] = key
self.data[pos] = value
self.slot_used += 1
def remove(self, key: int) -> int | None:
if self.slot_used == 0:
return None
pos = self.hash(key)