"""Hash tabuľka s lineárnym sondovaním a reťazcovými kľúčmi.""" _DELETED = object() # značka odstránenej položky class HashTable: def __init__(self, capacity=10): if type(capacity) is not int: raise TypeError("Kapacita musí byť celé číslo.") if capacity <= 0: raise ValueError("Kapacita musí byť kladná.") self._slots = [None] * capacity self._size = 0 @property def capacity(self): return len(self._slots) @staticmethod def _check_key(key): if not isinstance(key, str): raise TypeError("Kľúč musí byť reťazec.") def _is_active(self, index, key): slot = self._slots[index] return slot is not None and slot is not _DELETED and slot[0] == key def hash(self, key): """Vráť polynomiálny hash kľúča modulo aktuálna kapacita.""" self._check_key(key) h = 0 for char in key: h = (31 * h + ord(char)) % self.capacity return h def probe(self, key): """Vráť index kľúča alebo miesta na vloženie bez zmeny tabuľky.""" start = self.hash(key) first_deleted = None for step in range(self.capacity): index = (start + step) % self.capacity slot = self._slots[index] if slot is None: return first_deleted if first_deleted is not None else index if slot is _DELETED: if first_deleted is None: first_deleted = index elif slot[0] == key: return index if first_deleted is not None: return first_deleted raise RuntimeError("Tabuľka je plná.") def load_factor(self): """Vráť podiel počtu aktívnych položiek a kapacity.""" return len(self) / self.capacity def _resize(self, new_capacity): old_slots = self._slots self._slots = [None] * new_capacity for slot in old_slots: if slot is not None and slot is not _DELETED: self._slots[self.probe(slot[0])] = slot def __getitem__(self, key): index = self.probe(key) if not self._is_active(index, key): raise KeyError(key) return self._slots[index][1] def __setitem__(self, key, value): index = self.probe(key) if self._is_active(index, key): self._slots[index] = (key, value) return if (self._size + 1) / self.capacity > 0.7: self._resize(self.capacity * 2) index = self.probe(key) self._slots[index] = (key, value) self._size += 1 def __delitem__(self, key): index = self.probe(key) if not self._is_active(index, key): raise KeyError(key) self._slots[index] = _DELETED self._size -= 1 def __contains__(self, key): return self._is_active(self.probe(key), key) def __len__(self): return self._size def __iter__(self): for slot in self._slots: if slot is not None and slot is not _DELETED: yield slot[0]