This commit is contained in:
Volodymyr Baliasnikov 2026-10-30 00:25:46 +01:00
parent ec93d04fb2
commit 94259ed3f8

173
u7/hash_table.py Normal file
View File

@ -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)