diff --git a/u7/hash_table.py b/u7/hash_table.py index f90f5e4..d9b77c0 100644 --- a/u7/hash_table.py +++ b/u7/hash_table.py @@ -1,24 +1,21 @@ class HashTable: + _DELETED = object() + def __init__(self, capacity=10): - """Vytvor prázdnu tabuľku s kladnou celočíselnou kapacitou.""" if type(capacity) is not int: - raise TypeError("It's not integer") + raise TypeError("Capacity must be an integer") if capacity <= 0: - raise ValueError("Capacity > 0") + raise ValueError("Capacity must be positive") self._capacity = capacity self._table = [None] * capacity self._size = 0 - - @property def capacity(self): - """Vráť aktuálny počet pozícií v poli.""" - return self.kapacity + return self._capacity def hash(self, key): - """Vráť polynomiálny hash kľúča modulo aktuálna kapacita.""" if not isinstance(key, str): raise TypeError("Key must be a string") @@ -28,30 +25,105 @@ class HashTable: return h def probe(self, key): - """Vráť index kľúča alebo miesta na vloženie bez zmeny tabuľky.""" if not isinstance(key, str): raise TypeError("Key must be a string") start = self.hash(key) first_deleted = None - def load_factor(self): - """Vráť podiel počtu aktívnych položiek a kapacity.""" - if not isinstance(key, str): - raise TypeError("Key must be a string") + for krok in range(self.capacity): + index = (start + krok) % self.capacity + item = self._table[index] - def __getitem__(self, key): - if not isinstance(key, str): - raise TypeError("Key must be a string") + if item is None: + if first_deleted is not None: + return first_deleted + return index + + if item is self._DELETED: + if first_deleted is None: + first_deleted = index + elif item[0] == key: + return index + + if first_deleted is not None: + return first_deleted + + raise RuntimeError("Hash table is full") def __setitem__(self, key, value): if not isinstance(key, str): raise TypeError("Key must be a string") + try: + index = self.probe(key) + except RuntimeError: + index = None + + if index is not None: + item = self._table[index] + if item is not None and item is not self._DELETED: + if item[0] == key: + self._table[index] = (key, value) + return + + if (self._size + 1) / self.capacity > 0.7: + old_table = self._table + + self._capacity *= 2 + self._table = [None] * self._capacity + self._size = 0 + + for item in old_table: + if item is not None and item is not self._DELETED: + old_key, old_value = item + new_index = self.probe(old_key) + self._table[new_index] = (old_key, old_value) + self._size += 1 + + index = self.probe(key) + self._table[index] = (key, value) + self._size += 1 + + def __getitem__(self, key): + if not isinstance(key, str): + raise TypeError("Key must be a string") + + try: + index = self.probe(key) + except RuntimeError: + raise KeyError(key) + + item = self._table[index] + + if item is None or item is self._DELETED: + raise KeyError(key) + + if item[0] != key: + raise KeyError(key) + + return item[1] + def __delitem__(self, key): if not isinstance(key, str): raise TypeError("Key must be a string") + try: + index = self.probe(key) + except RuntimeError: + raise KeyError(key) + + item = self._table[index] + + if item is None or item is self._DELETED: + raise KeyError(key) + + if item[0] != key: + raise KeyError(key) + + self._table[index] = self._DELETED + self._size -= 1 + def __contains__(self, key): if not isinstance(key, str): raise TypeError("Key must be a string") @@ -62,11 +134,13 @@ class HashTable: return False item = self._table[index] + return ( item is not None and item is not self._DELETED and item[0] == key ) + def __len__(self): return self._size