From 94259ed3f8465f28cebe4b1198b760fed58fdc23 Mon Sep 17 00:00:00 2001 From: Volodymyr Baliasnikov Date: Fri, 30 Oct 2026 00:25:46 +0100 Subject: [PATCH] 1try --- u7/hash_table.py | 173 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 u7/hash_table.py diff --git a/u7/hash_table.py b/u7/hash_table.py new file mode 100644 index 0000000..2cda756 --- /dev/null +++ b/u7/hash_table.py @@ -0,0 +1,173 @@ +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) \ No newline at end of file