avp26/u7/hash_table.py
Your Name 41a315cf93 U7
2026-10-29 23:03:48 +01:00

109 lines
3.1 KiB
Python

class HashTable:
def __init__(self, capacity=10):
if type(capacity) is not int or isinstance(capacity, bool):
raise TypeError()
if capacity <= 0:
raise ValueError()
self._capacity = capacity
self.empty = object()
self.deleted = object()
self.slots = [self.empty] * capacity
self._size = 0
@property
def capacity(self):
return self._capacity
def hash(self, key):
if not isinstance(key, str):
raise TypeError()
h = 0
for ch in key:
h = (31 * h + ord(ch)) % self._capacity
return h
def probe(self, key):
if not isinstance(key, str):
raise TypeError()
start = self.hash(key)
first_deleted = None
for step in range(self._capacity):
curr = (start + step) % self._capacity
item = self.slots[curr]
if item is self.empty:
return first_deleted if first_deleted is not None else curr
elif item is self.deleted:
if first_deleted is None:
first_deleted = curr
elif item[0] == key:
return curr
if first_deleted is not None:
return first_deleted
raise RuntimeError()
def load_factor(self):
return self._size / self._capacity
def __getitem__(self, key):
try:
pos = self.probe(key)
except RuntimeError:
raise KeyError(key)
item = self.slots[pos]
if isinstance(item, tuple) and item[0] == key:
return item[1]
raise KeyError(key)
def __setitem__(self, key, value):
try:
pos = self.probe(key)
if isinstance(self.slots[pos], tuple) and self.slots[pos][0] == key:
self.slots[pos] = (key, value)
return
except RuntimeError:
pass
if (self._size + 1) / self._capacity > 0.7:
old_slots = self.slots
self._capacity *= 2
self.slots = [self.empty] * self._capacity
self._size = 0
for item in old_slots:
if isinstance(item, tuple):
pos = self.probe(item[0])
self.slots[pos] = item
self._size += 1
pos = self.probe(key)
self.slots[pos] = (key, value)
self._size += 1
def __delitem__(self, key):
try:
pos = self.probe(key)
except RuntimeError:
raise KeyError(key)
item = self.slots[pos]
if isinstance(item, tuple) and item[0] == key:
self.slots[pos] = self.deleted
self._size -= 1
return
raise KeyError(key)
def __contains__(self, key):
try:
pos = self.probe(key)
except RuntimeError:
return False
item = self.slots[pos]
return isinstance(item, tuple) and item[0] == key
def __len__(self):
return self._size
def __iter__(self):
for item in self.slots:
if isinstance(item, tuple):
yield item[0]