class HashTable: def __init__(self, capacity=10): """Vytvor prázdnu tabuľku s kladnou celočíselnou kapacitou.""" # Проверяем тип capacity он не должен быть bool, должен быть int if type(capacity) is not int: raise TypeError("Kapacita musi byt cele cislo.") # capacity должна быть > 0 if capacity <= 0: raise ValueError("Kapacita musi byt kladne cislo.") self._capacity = capacity self.empty = object() # Создаем обьект для пустых ячеек self.deleted = object() # Создаем обьект для удаленных ячеек self.slots = [self.empty] * capacity # создаем пустые ячейки в виде списка self.size = 0 # Счетчик активных элементов @property def capacity(self): """Vráť aktuálny počet pozícií v poli.""" return self._capacity def valid_problem(self, key): # Ключи обязаны быть строками (str) если передан другой тип то выбрасываем исключение if not isinstance(key, str): raise TypeError("Kluc musi byt retazec") def hash(self, key): """Vráť polynomiálny hash kľúča modulo aktuálna kapacita.""" # Проверяем тип ключа self.valid_problem(key) result = 0 for char in key: # считаем хеш по формуле с сайта result = result * 31 + ord(char) # делам чтобы индекс строго входил в диапазон [0, capacity - 1] return result % self._capacity def probe(self, key): """Vráť index kľúča alebo miesta na vloženie bez zmeny tabuľky.""" # Проверяем тип ключа self.valid_problem(key) # Получаем начальный хэш-индекс для данного ключа start_idx = self.hash(key) first_deleted_idx = None for step in range(self._capacity): # Вычисляем текущий индекс со сдвигом step и цикличным переходом через край массива (% capacity) idx = (start_idx + step) % self._capacity slot = self.slots[idx] if slot is self.empty: # Если ранее встретили удаленную ячейку возвращаем ее индекс if first_deleted_idx is not None: return first_deleted_idx # Иначе возвращаем индекс этой первой чистой ячейки return idx if slot is self.deleted: # Запоминаем индекс первой такой ячейки если еще не запомнили ранее if first_deleted_idx is None: first_deleted_idx = idx # Изолированно продолжаем цикл дальше так как ключ может быть в другой ячейке # Активная пара (key, value) elif slot[0] == key: # Если нашли ячейку с ключом возвращаем ее индекс return idx if first_deleted_idx is not None: return first_deleted_idx # Если массив raise RuntimeError("Tabulka je plna.") def load_factor(self): """Vráť podiel počtu aktívnych položiek a kapacity.""" factor = self.size / self._capacity return factor def __getitem__(self, key): # Проверяем тип ключа self.valid_problem(key) # Находим индекс потенциального местонахождения ключа 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] # Если ключа в таблице нет KeyError raise KeyError(key) def __setitem__(self, key, value): # Проверяем тип ключа self.valid_problem(key) # Находим нужный индекс через линейное зондирование idx = self.probe(key) slot = self.slots[idx] # Обновление значения существующего ключа if slot is not self.empty and slot is not self.deleted and slot[0] == key: # Просто перезаписываем кортеж (key, value) новым значением self.slots[idx] = (key, value) return # Вставляем новый ключи и проверяем порог заполнения > 0.7 if (self.size + 1) / self._capacity > 0.7: # Удваиваем таблицу и пересчитываем индексы old_slots = self.slots self._capacity = self._capacity * 2 self.slots = [self.empty] * self._capacity self.size = 0 for slot_item in old_slots: if slot_item is not self.empty and slot_item is not self.deleted: k, v = slot_item idx_new = self.probe(k) self.slots[idx_new] = (k, v) self.size += 1 # Перевычисляем индекс для вставляемого ключа уже в новой таблице idx = self.probe(key) # Записываем (key, value) в выбранную ячейку self.slots[idx] = (key, value) # Увеличиваем количество активных элементов на 1 self.size += 1 def __delitem__(self, key): # Проверяем тип ключа self.valid_problem(key) # Находим индекс элемента через probe idx = self.probe(key) slot = self.slots[idx] # Если ключ найден среди активных элементов if slot is not self.empty and slot is not self.deleted and slot[0] == key: # Заменяем пару (key, value) на маркер удаленной ячейки self.deleted self.slots[idx] = self.deleted # Уменьшаем количество активных элементов на 1 self.size -= 1 else: # Если ключ отсутствует KeyError raise KeyError(key) def __contains__(self, key): # Проверяем тип ключа self.valid_problem(key) # Находим индекс ячейки idx = self.probe(key) slot = self.slots[idx] # Возвращаем True только если ячейка активна и ключ совпадает return slot is not self.empty and slot is not self.deleted and slot[0] == key def __len__(self): return self.size def __iter__(self): active_keys = [] for slot in self.slots: # Проверка фильтрует незанятые и удаленные ячейки пропускает только те слоты где лежит реальная пара if slot is not self.empty and slot is not self.deleted: # Извлекается ключ (элемент с индексом 0) и добавляется в список active_keys.append(slot[0]) return iter(active_keys)