pygorithm/hash_map/hash_map.py

83 lines
3.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""
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)