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