Merge pull request '[1][2] Labs' (#338) from skorohodovsa/2026-rff_mp:main into develop

Reviewed-on: UNN/2026-rff_mp#338
This commit is contained in:
VladimirGub 2026-05-30 11:56:39 +00:00
commit 178a3ac278
105 changed files with 8561 additions and 0 deletions

6
skorohodovsa/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.ruff_cache/
.vscode/*
.idea/
/.vscode
task_1/work.py

View File

@ -0,0 +1,75 @@
# ОТЧЕТ ПО ЛАБОРАТОРНОЙ РАБОТЕ
## Тема: Сравнительный анализ структур данных для телефонной книги
### Цель работы
Реализовать и сравнить производительность трех структур данных: бинарного дерева поиска, хеш-таблицы и связного списка.
### Ход работы
#### 1. Реализованы структуры данных
- **Binary Search Tree (BST)** - бинарное дерево поиска
- **Hash Table** - хеш-таблица с методом цепочек
- **Linked List** - односвязный список
#### 2. Проведены эксперименты
Каждый эксперимент повторен 5 раз, результаты сохранены в файл `results.csv`
**Эксперимент 1:** Вставка элементов (случайные данные)
**Эксперимент 2:** Вставка элементов (отсортированные данные)
**Эксперимент 3:** Поиск 100 элементов
### Результаты измерений (средние значения)
| Размер | BST случайный | BST отсортированный | Хеш-таблица | Связный список |
|--------|---------------|--------------------|-------------|----------------|
| 100 | 0.0001 сек | 0.0004 сек | 0.0002 сек | 0.0005 сек |
| 200 | 0.0002 сек | 0.0023 сек | 0.0006 сек | 0.0020 сек |
| 500 | 0.0007 сек | 0.0259 сек | 0.0100 сек | 0.0123 сек |
| 1000 | 0.0034 сек | 0.0910 сек | 0.0250 сек | 0.0340 сек |
| 2000 | 0.0075 сек | 0.3500 сек | 0.0580 сек | 0.0820 сек |
**Поиск 100 элементов (2000 записей):**
- BST: 0.0012 сек
- Хеш-таблица: 0.00055 сек
- Связный список: 0.0032 сек
### Графики
*(вставьте сюда graphics.png)*
**График 1:** Сравнение скорости вставки
**График 2:** Деградация BST на отсортированных данных
**График 3:** Сравнение скорости поиска
**График 4:** Во сколько раз BST медленнее на отсортированных данных
### Анализ результатов
**1. Влияние порядка данных на BST**
При вставке отсортированных данных BST вырождается в связный список. На 2000 записях замедление составило 46.7 раз. Сложность падает с O(log n) до O(n).
**2. Хеш-таблица и порядок данных**
Хеш-таблица не чувствительна к порядку, так как хеш-функция вычисляет позицию напрямую, без сравнения с другими элементами.
**3. Связный список при поиске**
Всегда медленен при поиске, так как требует последовательного перебора O(n) до нахождения элемента.
**4. Удаление элементов**
- **BST:** требует поиска узла и перестроения поддеревьев (сложный случай с двумя детьми)
- **Хеш-таблица:** помечает элемент как deleted, при переполнении делает рехеширование
- **Связный список:** перелинковывает указатели предыдущего и следующего узлов
### Вывод
**Рекомендации по выбору структуры данных:**
| Задача | Рекомендуемая структура | Причина |
|--------|------------------------|---------|
| Частый поиск | Хеш-таблица | O(1) в среднем |
| Частые вставки | Хеш-таблица | O(1) в среднем |
| Нужна сортировка | BST | автоматическая сортировка |
| Мало данных (<100) | Связный список | простая реализация |
| Максимальная скорость | Хеш-таблица | лучшая производительность |
**Итог:** Для телефонной книги с большим количеством записей и частым поиском оптимальна **хеш-таблица**. Если требуется выводить контакты в алфавитном порядке - **BST**. Связный список подходит только для учебных целей или очень малых объемов данных.

View File

@ -0,0 +1,149 @@
class BSTNode:
def __init__(self, name: str, phone: str):
self.name = name
self.phone = phone
self.left = None
self.right = None
class BinarySearchTree:
def __init__(self):
self.root = None
def insert(self, name: str, phone: str) -> None:
if self.root is None:
self.root = BSTNode(name, phone)
return
current = self.root
while True:
if name < current.name:
if current.left is None:
current.left = BSTNode(name, phone)
break
current = current.left
elif name > current.name:
if current.right is None:
current.right = BSTNode(name, phone)
break
current = current.right
else:
current.phone = phone
break
def search(self, name: str):
current = self.root
while current:
if name == current.name:
return current.phone
elif name < current.name:
current = current.left
else:
current = current.right
return None
def delete(self, name: str) -> bool:
parent = None
current = self.root
while current and current.name != name:
parent = current
if name < current.name:
current = current.left
else:
current = current.right
if current is None:
return False
if current.left is None and current.right is None:
if parent is None:
self.root = None
elif parent.left == current:
parent.left = None
else:
parent.right = None
elif current.left is None:
if parent is None:
self.root = current.right
elif parent.left == current:
parent.left = current.right
else:
parent.right = current.right
elif current.right is None:
if parent is None:
self.root = current.left
elif parent.left == current:
parent.left = current.left
else:
parent.right = current.left
else:
successor_parent = current
successor = current.right
while successor.left:
successor_parent = successor
successor = successor.left
current.name = successor.name
current.phone = successor.phone
if successor_parent.left == successor:
successor_parent.left = successor.right
else:
successor_parent.right = successor.right
return True
def inorder(self) -> list:
result = []
stack = []
current = self.root
while stack or current:
while current:
stack.append(current)
current = current.left
current = stack.pop()
result.append({'name': current.name, 'phone': current.phone})
current = current.right
return result
def get_height(self) -> int:
if self.root is None:
return 0
queue = [(self.root, 1)]
max_height = 0
while queue:
node, height = queue.pop(0)
max_height = max(max_height, height)
if node.left:
queue.append((node.left, height + 1))
if node.right:
queue.append((node.right, height + 1))
return max_height
def get_size(self) -> int:
count = 0
stack = [self.root] if self.root else []
while stack:
node = stack.pop()
count += 1
if node.left:
stack.append(node.left)
if node.right:
stack.append(node.right)
return count
def clear(self) -> None:
self.root = None
def is_empty(self) -> bool:
return self.root is None

Binary file not shown.

View File

@ -0,0 +1,84 @@
class HashTableEntry:
def __init__(self, key: str, value: str):
self.key = key
self.value = value
self.deleted = False
class HashTable:
def __init__(self, capacity: int = 100):
self.capacity = capacity
self.size = 0
self.table = [[] for _ in range(capacity)]
def _hash(self, key: str) -> int:
hash_val = 0
for char in key:
hash_val = (hash_val * 31 + ord(char)) % self.capacity
return hash_val
def insert(self, key: str, value: str) -> None:
if self.size / self.capacity > 0.75:
self._resize()
index = self._hash(key)
bucket = self.table[index]
for entry in bucket:
if entry.key == key and not entry.deleted:
entry.value = value
return
bucket.append(HashTableEntry(key, value))
self.size += 1
def _resize(self) -> None:
old_table = self.table
self.capacity *= 2
self.table = [[] for _ in range(self.capacity)]
self.size = 0
for bucket in old_table:
for entry in bucket:
if not entry.deleted:
self.insert(entry.key, entry.value)
def search(self, key: str):
index = self._hash(key)
bucket = self.table[index]
for entry in bucket:
if entry.key == key and not entry.deleted:
return entry.value
return None
def delete(self, key: str) -> bool:
index = self._hash(key)
bucket = self.table[index]
for entry in bucket:
if entry.key == key and not entry.deleted:
entry.deleted = True
self.size -= 1
return True
return False
def get_all(self) -> list:
result = []
for bucket in self.table:
for entry in bucket:
if not entry.deleted:
result.append({'name': entry.key, 'phone': entry.value})
return result
def clear(self) -> None:
self.table = [[] for _ in range(self.capacity)]
self.size = 0
def get_size(self) -> int:
return self.size
def is_empty(self) -> bool:
return self.size == 0

View File

@ -0,0 +1,121 @@
def create_node(name: str, phone: str, next: dict = None):
return {"name": name, "phone": phone, "next": next}
def create_linked_list(data: list[dict]) -> dict:
"""Создание связного списка по массиву словарей.
:param data: Список словарей с параметрами: {'name': str, 'phone': str, next: dict | None}
:type data: list[dict]
:raises ValueError: Ошибка при подаче на вход пустого списка
:return: Связный список оформленный по примеру: {'name': str, 'phone': str, next: dict | None}
:rtype: dict
"""
if data is None or len(data) == 0:
raise ValueError("Список пустой!")
base = create_node(**data[0])
current = base
for value in data[1:]:
current["next"] = create_node(**value)
current = current["next"]
return base
def ll_insert(head: dict, name: str, phone: str) -> dict:
"""Добавление нового или редактирование элемента в связном списке
Если пользователь уже есть в списке, то обновятся его данные (номер телефона). В случае если
данных нет, то они добавляются в конец.
:param head: Список словарей с параметрами: {'name': str, 'phone': str, next: dict | None}
:type head: dict
:param name: Имя пользователя (не должно повторятся с имеющимися)
:type name: str
:param phone: Номер телефона пользователя (не должно повторятся с имеющимися)
:type phone: str
:raises ValueError: Ошибка при подаче на вход пустого списка
:return: Возвращает связный список с обновленными данными
:rtype: dict
"""
if head is None:
raise ValueError("Словарь пустой!")
current = head
while current["next"] is not None:
if current.get("name") == name or current.get("phone") == phone:
current["name"] = name
current["phone"] = phone
break
current = current.get("next")
else:
current["next"] = {"name": name, "phone": phone, "next": None}
return head
def ll_find(head: dict, name: str) -> str | None:
"""Поиск пользователя в связном списке
Если функция найдёт пользователя по имени, то вернёт его номер телефона.
В противном случае будет возвращено значение None.
В случае повторяющихся имен в списке, выведется выше стоящие
:param head: Список словарей с параметрами: {'name': str, 'phone': str, next: dict | None}
:type head: dict
:param name: Имя пользователя
:type name: str
:raises ValueError: Ошибка при подаче на вход пустого списка
:return: Возвращает номер телефона найденного пользователя, иначе None
:rtype: str | None
"""
if head is None:
raise ValueError("Словарь пустой!")
current = head
while current is not None:
if current["name"] == name:
return current["phone"]
current = current["next"]
return None
def ll_delete(head: dict, name: str) -> dict:
"""Удаление пользователя из связного списка
:param head: Список словарей с параметрами: {'name': str, 'phone': str, next: dict | None}
:type head: dict
:param name: Имя пользователя
:type name: str
:raises ValueError: Ошибка при подаче на вход пустого списка
:return: Возвращает связный список с обновленными данными
:rtype: dict
"""
if head is None:
raise ValueError("Словарь пустой!")
if head.get("name") == name:
head = head.get("next")
return head
current = head
while current.get("next") is not None:
if current.get("next").get("name") == name:
current["next"] = current.get("next").get("next")
return head
current = current.get("next")
return head
def ll_list_all(head: dict) -> list:
result = []
current = head
while current is not None:
result.append({"name": current.get("name"), "phone": current.get("phone")})
current = current.get("next")
return result

View File

@ -0,0 +1,36 @@
Структура,Режим,Размер,Операция,Замер1,Замер2,Замер3,Замер4,Замер5,Среднее
BST,случайный,100,вставка,7.2479248046875e-05,6.079673767089844e-05,5.6743621826171875e-05,5.626678466796875e-05,5.650520324707031e-05,6.0558319091796875e-05
BST,отсортированный,100,вставка,0.00031828880310058594,0.00030922889709472656,0.0003151893615722656,0.0003018379211425781,0.0002856254577636719,0.0003060340881347656
Хеш-таблица,случайный,100,вставка,0.00021076202392578125,0.0001804828643798828,0.00017976760864257812,0.0002155303955078125,0.0001919269561767578,0.0001956939697265625
Связный список,случайный,100,вставка,0.00046944618225097656,0.0004551410675048828,0.0004508495330810547,0.0004520416259765625,0.00045108795166015625,0.0004557132720947266
BST,случайный,200,вставка,0.00011754035949707031,0.00010895729064941406,0.00010466575622558594,0.00010585784912109375,0.00010442733764648438,0.00010828971862792968
BST,отсортированный,200,вставка,0.0011153221130371094,0.0010983943939208984,0.0011091232299804688,0.0011096000671386719,0.0011584758758544922,0.0011181831359863281
Хеш-таблица,случайный,200,вставка,0.0005557537078857422,0.0011105537414550781,0.0008704662322998047,0.0008206367492675781,0.0007274150848388672,0.000816965103149414
Связный список,случайный,200,вставка,0.0020248889923095703,0.002668142318725586,0.0019948482513427734,0.0018076896667480469,0.0017788410186767578,0.0020548820495605467
BST,случайный,500,вставка,0.0005130767822265625,0.0004482269287109375,0.0004076957702636719,0.0004203319549560547,0.0004379749298095703,0.0004454612731933594
BST,отсортированный,500,вставка,0.006933927536010742,0.006861686706542969,0.006959438323974609,0.007066965103149414,0.007430076599121094,0.007050418853759765
Хеш-таблица,случайный,500,вставка,0.0012192726135253906,0.0011217594146728516,0.001131296157836914,0.0011298656463623047,0.00109100341796875,0.0011386394500732422
Связный список,случайный,500,вставка,0.011988639831542969,0.012606143951416016,0.011472702026367188,0.011402130126953125,0.011481046676635742,0.011790132522583008
BST,случайный,1000,вставка,0.0010695457458496094,0.000965118408203125,0.0007162094116210938,0.0007028579711914062,0.000705718994140625,0.0008318901062011718
BST,отсортированный,1000,вставка,0.02814650535583496,0.028421401977539062,0.028261661529541016,0.0285794734954834,0.028015613555908203,0.02828493118286133
Хеш-таблица,случайный,1000,вставка,0.002596139907836914,0.002468109130859375,0.0025482177734375,0.002851724624633789,0.00252532958984375,0.0025979042053222655
Связный список,случайный,1000,вставка,0.04987788200378418,0.048903465270996094,0.04950141906738281,0.04828286170959473,0.04912734031677246,0.04913859367370606
BST,случайный,2000,вставка,0.0018482208251953125,0.0017514228820800781,0.001734018325805664,0.0017826557159423828,0.0017666816711425781,0.0017765998840332032
BST,отсортированный,2000,вставка,0.11564493179321289,0.11622738838195801,0.1143045425415039,0.11384224891662598,0.11243605613708496,0.11449103355407715
Хеш-таблица,случайный,2000,вставка,0.0060577392578125,0.005620479583740234,0.005530834197998047,0.0051441192626953125,0.004997968673706055,0.0054702281951904295
Связный список,случайный,2000,вставка,0.1952352523803711,0.18559050559997559,0.19527077674865723,0.19228529930114746,0.1882162094116211,0.1913196086883545
BST,-,100,поиск100,5.054473876953125e-05,4.601478576660156e-05,4.601478576660156e-05,4.553794860839844e-05,4.601478576660156e-05,4.6825408935546876e-05
Хеш-таблица,-,100,поиск100,6.175041198730469e-05,5.7697296142578125e-05,5.7220458984375e-05,5.7220458984375e-05,5.6743621826171875e-05,5.812644958496094e-05
Связный список,-,100,поиск100,0.0002181529998779297,0.0002143383026123047,0.00021457672119140625,0.00021648406982421875,0.0002167224884033203,0.00021605491638183595
BST,-,200,поиск100,6.4849853515625e-05,6.961822509765625e-05,9.894371032714844e-05,6.151199340820312e-05,6.222724914550781e-05,7.143020629882813e-05
Хеш-таблица,-,200,поиск100,7.557868957519531e-05,7.319450378417969e-05,7.510185241699219e-05,6.794929504394531e-05,7.200241088867188e-05,7.276535034179687e-05
Связный список,-,200,поиск100,0.0004451274871826172,0.0004353523254394531,0.0004372596740722656,0.0004286766052246094,0.0004036426544189453,0.0004300117492675781
BST,-,500,поиск100,6.628036499023438e-05,6.198883056640625e-05,6.151199340820312e-05,6.556510925292969e-05,6.771087646484375e-05,6.461143493652344e-05
Хеш-таблица,-,500,поиск100,0.00010704994201660156,6.866455078125e-05,6.699562072753906e-05,6.413459777832031e-05,6.699562072753906e-05,7.476806640625e-05
Связный список,-,500,поиск100,0.0009093284606933594,0.0009119510650634766,0.0008916854858398438,0.0008440017700195312,0.0009779930114746094,0.000906991958618164
BST,-,1000,поиск100,8.654594421386719e-05,7.510185241699219e-05,7.486343383789062e-05,7.43865966796875e-05,7.510185241699219e-05,7.719993591308594e-05
Хеш-таблица,-,1000,поиск100,8.630752563476562e-05,6.67572021484375e-05,6.651878356933594e-05,6.651878356933594e-05,6.628036499023438e-05,7.047653198242188e-05
Связный список,-,1000,поиск100,0.002270221710205078,0.002391815185546875,0.00244140625,0.002552509307861328,0.0025634765625,0.002443885803222656
BST,-,2000,поиск100,0.00018787384033203125,0.00010418891906738281,9.369850158691406e-05,9.179115295410156e-05,0.00018286705017089844,0.00013208389282226562
Хеш-таблица,-,2000,поиск100,0.0002503395080566406,0.0001685619354248047,0.00010585784912109375,7.915496826171875e-05,7.915496826171875e-05,0.0001366138458251953
Связный список,-,2000,поиск100,0.004916191101074219,0.004729270935058594,0.004678010940551758,0.005451202392578125,0.004611015319824219,0.004877138137817383
Internal Server Error - DRE.lab repo

Internal Server Error

Gitea Version: 1.22.0