From c334dcca73aea53504ac0f13cd43360b2c404202 Mon Sep 17 00:00:00 2001 From: ialina07 Date: Sat, 20 Dec 2025 15:59:07 +0300 Subject: [PATCH 1/2] Create hash_table.py --- src/hash_table.py | 128 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/hash_table.py diff --git a/src/hash_table.py b/src/hash_table.py new file mode 100644 index 0000000..27cedd9 --- /dev/null +++ b/src/hash_table.py @@ -0,0 +1,128 @@ +class HashNode: + """Для хранения пары ключ-значения в цепочке""" + + def __init__(self, key, value): + self.key = key + self.value = value + self.next = None + + +class HashTable: + """Хеш-таблица с использованием метода цепочек для разрешения коллизий""" + + def __init__(self, size=128): # инициализация хеш-таблицы + self.size = size + self.table = [None] * size + self.count = 0 + + def _hash(self, key): # хеш-функция для строковых ключей + + if not isinstance(key, str): + raise TypeError("Ключ должен быть строкой") + + hash.value = 0 + for i in key: + hash_value = (hash_value * 31 + ord(i)) % self.size + + return hash_value + + def get(self, key, default=None): # получение значения по ключу + index = self._hash(key) + current = self.table[index] + + # ищем ключ в цепочке + while current: + if current.key == key: + return current.value + current = current.next + + # Ключ не найден + return default + + + def put(self, key, value): # добавление значения + index = self._hash(key) + + #если ячейка пуста, создаем новый узел + if self.table[index] is None: + self.table[index] = HashNode(key, value) + self.count += 1 + return + + #ищем ключ в цепочке + current = self.table[index] + while current: + if current.key == key: + current.value = value + return + if current.next is None: + break + current = current.next + + # если ключ не найден, добавляем новый узел в конец цепочки + current.next = HashNode(key, value) + self.count += 1 + + def remove(self, key): + """Удаление пары ключ-значение""" + index = self._hash(key) + current = self.table[index] + pr = None + + # ищем ключ в цепочке + while current: + if current.key == key: + # удаляем узел из цепочки + if pr: + pr.next = current.next + else: + self.table[index] = current.next + self.count -= 1 + return current.value + + pr = current + current = current.next + + #ключ не найден + raise KeyError("Ключ не найден") + + def __contains__(self, key): # проверка наличия ключа в таблице + return self.get(key) is not None + + def __len__(self): # количество элементов в таблице + return self.count + + def keys(self): + """Генератор всех ключей в таблице""" + for head in self.table: + current = head + while current: + yield current.key + current = current.next + + def values(self): + """Генератор всех значений в таблице""" + for head in self.table: + current = head + while current: + yield current.value + current = current.next + + def items(self): + """Генератор всех пар ключ-значение в таблице""" + for head in self.table: + current = head + while current: + yield (current.key, current.value) + current = current.next + + def clear(self): + """Очистка таблицы""" + self.table = [None] * self.size + self.count = 0 + + + + + + From 8559d93c9886cbf5403a00dfd86be0447a8586e7 Mon Sep 17 00:00:00 2001 From: ialina07 Date: Sat, 20 Dec 2025 16:03:41 +0300 Subject: [PATCH 2/2] Create tests_hash_table.py --- src/tests_hash_table.py | 136 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 src/tests_hash_table.py diff --git a/src/tests_hash_table.py b/src/tests_hash_table.py new file mode 100644 index 0000000..e54f195 --- /dev/null +++ b/src/tests_hash_table.py @@ -0,0 +1,136 @@ +import pytest +from hash_table import HashTable + + +class TestHashTable: + """Тесты для класса HashTable""" + + def test_init(self): # тест инициализации таблицы + ht = HashTable() + assert len(ht) == 0 + assert ht.size == 128 + + def test_put_and_get(self): # тест добавления и получения элементов + ht = HashTable() + + # Добавление элементов + ht.put("key1", "value1") + ht.put("key2", 123) + ht.put("key3", [1, 2, 3]) + + # Проверка получения + assert ht.get("key1") == "value1" + assert ht.get("key2") == 123 + assert ht.get("key3") == [1, 2, 3] + assert len(ht) == 3 + + def test_update_value(self): + """Тест обновления значения существующего ключа""" + ht = HashTable() + + ht.put("key1", "old_value") + assert ht.get("key1") == "old_value" + + ht.put("key1", "new_value") + assert ht.get("key1") == "new_value" + assert len(ht) == 1 # Количество не должно измениться + + def test_get_nonexistent(self): + """Тест получения несуществующего ключа""" + ht = HashTable() + + assert ht.get("nonexistent") is None + assert ht.get("nonexistent", "default") == "default" + + def test_remove(self): + """Тест удаления элементов""" + ht = HashTable() + + ht.put("key1", "value1") + ht.put("key2", "value2") + + # Удаление существующего ключа + removed_value = ht.remove("key1") + assert removed_value == "value1" + assert len(ht) == 1 + assert ht.get("key1") is None + + # Удаление несуществующего ключа + with pytest.raises(KeyError): + ht.remove("nonexistent") + + def test_contains(self): + """Тест проверки наличия ключа""" + ht = HashTable() + + ht.put("key1", "value1") + + assert "key1" in ht + assert "nonexistent" not in ht + + def test_collision_handling(self): + """Тест обработки коллизий""" + ht = HashTable(size=5) # Маленький размер для теста коллизий + + # Добавляем ключи, которые могут вызывать коллизии + ht.put("ab", "value1") + ht.put("ba", "value2") # Может создать коллизию + + # Проверяем, что оба значения доступны + assert ht.get("ab") == "value1" + assert ht.get("ba") == "value2" + + # Удаляем один из них + ht.remove("ab") + assert ht.get("ab") is None + assert ht.get("ba") == "value2" + + def test_clear(self): + """Тест очистки таблицы""" + ht = HashTable() + + ht.put("key1", "value1") + ht.put("key2", "value2") + + assert len(ht) == 2 + + ht.clear() + assert len(ht) == 0 + assert ht.get("key1") is None + assert ht.get("key2") is None + + def test_keys_values_items(self): + """Тест методов keys, values, items""" + ht = HashTable() + + ht.put("key1", "value1") + ht.put("key2", "value2") + ht.put("key3", "value3") + + # Проверяем keys + keys = list(ht.keys()) + assert set(keys) == {"key1", "key2", "key3"} + + # Проверяем values + values = list(ht.values()) + assert set(values) == {"value1", "value2", "value3"} + + # Проверяем items + items = dict(ht.items()) + assert items == {"key1": "value1", "key2": "value2", "key3": "value3"} + + def test_hash_function(self): + """Тест хэш-функции""" + ht = HashTable() + + # Хэш должен быть в пределах размера таблицы + for key in ["test", "hello", "world", "python"]: + hash_val = ht._hash(key) + assert 0 <= hash_val < ht.size + + # Проверяем ошибку для нестрокового ключа + with pytest.raises(TypeError): + ht._hash(123) + +if __name__ == "__main__": + pytest.main([__file__, "-v"])