1try
This commit is contained in:
parent
ec93d04fb2
commit
94259ed3f8
173
u7/hash_table.py
Normal file
173
u7/hash_table.py
Normal 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)
|
||||||
Loading…
Reference in New Issue
Block a user