avp26/u7/hash_table.py

127 lines
4.0 KiB
Python

class HashTable:
def __init__(self, capacity=10):
if type(capacity) is not int or isinstance(capacity, bool):
raise TypeError("Kapacita musí byť celé číslo.")
if capacity <= 0:
raise ValueError("Kapacita musí byť kladné celé číslo.")
self._capacity = capacity
self.empty = object()
self.deleted = object()
self.slots = [self.empty] * capacity
self.count = 0
@property
def capacity(self):
return self._capacity
def hash(self, key):
if not isinstance(key, str):
raise TypeError("Kľúč musí byť reťazec.")
h = 0
for znak in key:
h = (31 * h + ord(znak)) % self._capacity
return h
def probe(self, key):
if not isinstance(key, str):
raise TypeError("Kľúč musí byť reťazec.")
start_idx = self.hash(key)
first_deleted = None
for krok in range(self._capacity):
idx = (start_idx + krok) % self._capacity
slot = self.slots[idx]
if slot is self.empty:
return first_deleted if first_deleted is not None else idx
elif slot is self.deleted:
if first_deleted is None:
first_deleted = idx
elif slot[0] == key:
return idx
if first_deleted is not None:
return first_deleted
raise RuntimeError("Tabuľka je plná a nemá žiadne voľné ani vymazané miesto.")
def load_factor(self):
return self.count / self._capacity
def __getitem__(self, key):
if not isinstance(key, str):
raise TypeError("Kľúč musí byť reťazec.")
idx = self.probe(key)
slot = self.slots[idx]
if slot is not self.empty and slot is not self.deleted and slot[0] == key:
return slot[1]
raise KeyError(key)
def __setitem__(self, key, value):
if not isinstance(key, str):
raise TypeError("Kľúč musí byť reťazec.")
start_idx = self.hash(key)
existing_idx = None
for krok in range(self._capacity):
idx = (start_idx + krok) % self._capacity
slot = self.slots[idx]
if slot is self.empty:
break
if slot is not self.deleted and slot[0] == key:
existing_idx = idx
break
if existing_idx is not None:
self.slots[existing_idx] = (key, value)
return
if (self.count + 1) / self._capacity > 0.7:
self._resize(self._capacity * 2)
idx = self.probe(key)
self.slots[idx] = (key, value)
self.count += 1
def _resize(self, new_capacity):
old_slots = self.slots
self._capacity = new_capacity
self.slots = [self.empty] * new_capacity
self.count = 0
for slot in old_slots:
if slot is not self.empty and slot is not self.deleted:
k, v = slot
idx = self.probe(k)
self.slots[idx] = (k, v)
self.count += 1
def __delitem__(self, key):
if not isinstance(key, str):
raise TypeError("Kľúč musí byť reťazec.")
idx = self.probe(key)
slot = self.slots[idx]
if slot is not self.empty and slot is not self.deleted and slot[0] == key:
self.slots[idx] = self.deleted
self.count -= 1
return
raise KeyError(key)
def __contains__(self, key):
if not isinstance(key, str):
raise TypeError("Kľúč musí byť reťazec.")
try:
idx = self.probe(key)
except RuntimeError:
return False
slot = self.slots[idx]
return slot is not self.empty and slot is not self.deleted and slot[0] == key
def __len__(self):
return self.count
def __iter__(self):
for slot in self.slots:
if slot is not self.empty and slot is not self.deleted:
yield slot[0]