Compare commits

...

11 Commits

16 changed files with 555 additions and 5 deletions

3
.gitignore vendored
View File

@ -6,10 +6,7 @@ __pycache__/
# C extensions
*.so
<<<<<<< HEAD
=======
>>>>>>> a50fd0b ([0])
.DS_Store
# Distribution / packaging
.Python

145
README.md
View File

@ -16,7 +16,7 @@
### Крайний срок приема работ 25.05.2026 до 14:00
## Задание 1 -- репозиторий [отдельный срок на создание PR с папкой: 28.02.2026]
## Задание 0 -- репозиторий [отдельный срок на создание PR с папкой: 28.02.2026]
0. Создай пользователя (логин — фамилия+инициалы слитно транслитом, как в терминал-классе).
@ -54,3 +54,146 @@
- Сравниваемая ветка: **свой форк / IvanovII**
8. Отправь PR.
## Задание 1 -- структуры данных
***Напоминание: под каждое задание вы создаете отдельную ветку***
>Для оформления результатов заведи папку **docs** в своей папке и сохраняй туда отчет (в любом формате от .doc до .md, а то и .jpnb). Вспомогательные файлы клади в подпапку **data** внутри **docs**
**Цель работы**
Реализовать три различные структуры данных «с нуля», применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций. Вы должны собственными руками написать код, чтобы понять внутреннее устройство связного списка, хеш-таблицы и двоичного дерева поиска, а также осознать их сильные и слабые стороны на практике.
**!! Задание выполнять в структурной (процедурной) парадигме, не используя классы. Главное реализовать структуры данных «руками» и сравнить их производительность.**
### Базовые операции (обязательны для всех):
`insert(name, phone)` -- добавить или обновить запись.
`find(name)` -- phone или None.
`delete(name)` -- удалить запись, игнорировать отсутствие.
`list_all()` -- список всех записей, отсортированный по имени (для BST inorder обход; для списка и хеш‑таблицы — собрать и отсортировать явно).
#### 1. Связный список (LinkedListPhoneBook)
Узел представляется словарём: `{'name': 'Имя', 'phone': '123', 'next': None}.`
**Функции:**
`def ll_insert(head, name, phone)` — проходит до конца (или сразу добавляет в конец) и возвращает новую голову (если вставка в начало) или изменяет список по ссылке. Удобнее возвращать новую голову, если вставка может быть в начало.
`def ll_find(head, name)` — ищет узел, возвращает телефон или None.
`def ll_delete(head, name)` — удаляет узел, возвращает новую голову.
`def ll_list_all(head)` — собирает все записи в список и сортирует (сортировка вынесена отдельно).
#### 2. Хеш-таблица
Хранится как список buckets фиксированной длины, каждый элемент — голова связного списка (или None).
**Функции:**
`def ht_insert(buckets, name, phone)` — вычисляет индекс, вызывает ll_insert для соответствующего бакета.
Аналогично `ht_find, ht_delete, ht_list_all` (последняя собирает все записи из всех бакетов и сортирует).
#### 3. Двоичное дерево поиска
Узел — словарь: `{'name': 'Имя', 'phone': '123', 'left': None, 'right': None}.`
**Функции:**
`def bst_insert(root, name, phone)` — рекурсивно или итеративно вставляет, возвращает новый корень (если корень меняется).
`def bst_find(root, name)` — поиск.
`def bst_delete(root, name)` — удаление, возвращает новый корень.
`def bst_list_all(root)` — центрированный обход (рекурсивно собирает записи в отсортированном порядке).
### Экспериментальная часть (подробно об измерении времени)
#### 1. Генерация тестовых данных
Создайте список records из N элементов (например, N = 10000). Каждый элемент — кортеж (name, phone).
Имена генерируйте как `f"User_{i:05d}"` (равномерное распределение) или случайные слова из небольшого набора (чтобы были повторения и коллизии). Для проверки влияния порядка подготовьте два варианта одного и того же набора:
`records_shuffled` — случайный порядок.
`records_sorted` — отсортированный по имени (по алфавиту).
#### 2. Инструменты замера времени
Используйте модуль **time**:
```python
import time
start = time.perf_counter()
# ... операции ...
end = time.perf_counter()
elapsed = end - start # время в секундах
```
Для многократных замеров удобен `timeit`, но в этой задаче достаточно просто обернуть код в цикл и усреднить.
#### 3. Проведение замеров
Для каждой структуры данных и для каждого режима входных данных (случайный / отсортированный) выполните:
- А. Вставка всех записей
Создайте пустую структуру.
Засеките время, выполните insert для каждой записи из входного списка.
Зафиксируйте общее время вставки.
- Б. Поиск 100 случайных записей
Возьмите 100 случайных имён из того же набора (гарантированно существующих) и 10 имён, которых нет (например, "None_{i}").
Засеките время на выполнение всех 110 вызовов find.
- В. Удаление 50 случайных записей
Выберите 50 случайных имён из набора.
Засеките время на выполнение delete для каждого.
**!! Важно: после вставки структура остаётся заполненной, поиск и удаление выполняются на ней же. Если нужно повторить замер для другого порядка данных — создавайте новую структуру и заполняйте заново.**
#### 4. Сохранение результатов
**!! Каждый эксперимент повторить минимум 5 раз и записывать и среднее время, и все замеры.**
Соберите все замеры в словарь или список, затем сохраните в CSV-файл:
```python
import csv
results = [
["Структура", "Режим", "Операция", "Время (сек)"],
["LinkedList", "случайный", "вставка", 0.123],
...
]
with open("results.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerows(results)
```
#### 5. Анализ результатов
Постройте график (столбчатая диаграмма или линейный график) — можно в Excel, Google Sheets или с помощью matplotlib в Python.
Сравните:
- Как порядок входных данных влияет на скорость вставки в BST (деградация до O(n) на отсортированных данных).
- Почему хеш-таблица почти не чувствительна к порядку.
- Почему связный список всегда медленен при поиске.
- Как удаление работает в каждой структуре.
* Вывод должен содержать ответ на вопрос: какую структуру и для каких задач (частые вставки, частый поиск, необходимость получать данные в порядке) стоит выбирать в реальной жизни.*

View File

@ -0,0 +1,71 @@
#Узел — словарь: {'name': 'Имя', 'phone': '123', 'left': None, 'right': None}.
def bst_insert(root, name, phone):
#рекурсивно или итеративно вставляет, возвращает новый корень (если корень меняется).
if root is None:
return {'name': name,'phone': phone,'left': None,'right': None}
if name < root['name']:
root['left'] = bst_insert(root['left'], name, phone)
elif name > root['name']:
root['right'] = bst_insert(root['right'], name, phone)
else:
root['phone'] = phone
return root
def bst_find(root, name):
if root is None:
return None
if name == root['name']:
return root['phone']
if name < root['name']:
return bst_find(root['left'], name)
return bst_find(root['right'], name)
def bst_delete(root, name):
#удаление, возвращает новый корень.
if root is None:
return None
if name < root['name']:
root['left'] = bst_delete(root['left'], name)
elif name > root['name']:
root['right'] = bst_delete(root['right'], name)
else:
if root['left'] is None:
return root['right']
if root['right'] is None:
return root['left']
min_node = root['right']
while min_node['left'] is not None:
min_node = min_node['left']
root['name'] = min_node['name']
root['phone'] = min_node['phone']
root['right'] = bst_delete(root['right'], min_node['name'])
return root
def bst_list_all(root):
# центрированный обход (рекурсивно собирает записи в отсортированном порядке).
if root is None:
return []
return (
bst_list_all(root['left']) +
[(root['name'], root['phone'])] +
bst_list_all(root['right'])
)

View File

@ -0,0 +1,56 @@
#Аналогично ht_find, ht_delete, ht_list_all (последняя собирает все записи из всех бакетов и сортирует).
from code.LinkedList import *
def create_buckets(size=1000):
return [None] * size
def hash_function(name, bucket_size):
hash_value = 0
for char in name:
hash_value = (hash_value * 31 + ord(char)) % bucket_size
return hash_value
def ht_insert(buckets, name, phone):
#вычисляет индекс, вызывает ll_insert для соответствующего бакета.
if buckets is None:
buckets = create_buckets()
index = hash_function(name, len(buckets))
buckets[index] = ll_insert(buckets[index], name, phone)
return buckets
def ht_find(buckets, name):
if not buckets:
return None
index = hash_function(name, len(buckets))
return ll_find(buckets[index], name)
def ht_delete(buckets, name):
if not buckets:
return buckets
index = hash_function(name, len(buckets))
buckets[index] = ll_delete(buckets[index], name)
return buckets
def ht_list_all(buckets):
if not buckets:
return []
records = []
for bucket in buckets:
current = bucket
while current is not None:
records.append((current['name'], current['phone']))
current = current['next']
records.sort(key=lambda x: x[0])
return records

View File

@ -0,0 +1,62 @@
def ll_insert(head, name, phone):
#проходит до конца (или сразу добавляет в конец) и возвращает новую голову (если вставка в начало) или изменяет список по ссылке. Удобнее возвращать новую голову, если вставка может быть в начало.
if head is None:
return {'name': name, 'phone': phone, 'next': None}
current = head
while current is not None:
if current['name'] == name:
current['phone'] = phone
return head
if current['next'] is None:
current['next'] = {'name': name, 'phone': phone, 'next': None}
return head
current = current['next']
def ll_find(head, name):
# ищет узел, возвращает телефон или None.
current = head
while current is not None:
if current['name'] == name:
return current['phone']
current = current['next']
return None
def ll_delete(head, name):
# удаляет узел, возвращает новую голову.
if head is None:
return None
if head['name'] == name:
return head['next']
current = head
while current['next'] is not None:
if current['next']['name'] == name:
current['next'] = current['next']['next']
return head
current = current['next']
return head
def ll_list_all(head):
#собирает все записи в список и сортирует (сортировка вынесена отдельно).
result = []
current = head
while current is not None:
result.append((current['name'], current['phone']))
current = current['next']
result.sort(key=lambda x: x[0])
return result

View File

138
nehoroshevaa/Task 1/exp.py Normal file
View File

@ -0,0 +1,138 @@
import sys
sys.setrecursionlimit(20000)
import time
import random
import csv
from code.LinkedList import *
from code.HashTable import *
from code.BinaryTree import *
def generate_data(n=10000):
return [(f"User_{i:05d}", f"+7-900-{i:05d}") for i in range(n)]
def prepare_data(n=10000):
data = generate_data(n)
shuffled = data[:]
random.shuffle(shuffled)
sorted_data = sorted(data, key=lambda x: x[0])
return shuffled, sorted_data
def measure_find(find_func, obj, data):
exist = random.sample([x[0] for x in data], 100)
fake = [f"None_{i}" for i in range(10)]
names = exist + fake
start = time.perf_counter()
for name in names:
find_func(obj, name)
return time.perf_counter() - start
def measure_delete(delete_func, obj, data):
names = random.sample([x[0] for x in data], 50)
start = time.perf_counter()
for name in names:
obj = delete_func(obj, name)
return time.perf_counter() - start
def test_linked_list(data):
head = None
start = time.perf_counter()
for name, phone in data:
head = ll_insert(head, name, phone)
insert_time = time.perf_counter() - start
find_time = measure_find(ll_find, head, data)
delete_time = measure_delete(ll_delete, head, data)
return insert_time, find_time, delete_time
def test_hash_table(data):
buckets = create_buckets(1000)
start = time.perf_counter()
for name, phone in data:
ht_insert(buckets, name, phone)
insert_time = time.perf_counter() - start
find_time = measure_find(ht_find, buckets, data)
delete_time = measure_delete(ht_delete, buckets, data)
return insert_time, find_time, delete_time
def test_bst(data):
root = None
start = time.perf_counter()
for name, phone in data:
root = bst_insert(root, name, phone)
insert_time = time.perf_counter() - start
find_time = measure_find(bst_find, root, data)
delete_time = measure_delete(bst_delete, root, data)
return insert_time, find_time, delete_time
def run():
results = []
results.append(["Structure", "Order", "Insert", "Find", "Delete"])
for i in range(5):
shuffled, sorted_data = prepare_data()
# -------- LinkedList --------
i1, f1, d1 = test_linked_list(shuffled)
results.append(["LinkedList", "shuffled", i1, f1, d1])
i1, f1, d1 = test_linked_list(sorted_data)
results.append(["LinkedList", "sorted", i1, f1, d1])
# -------- HashTable --------
i2, f2, d2 = test_hash_table(shuffled)
results.append(["HashTable", "shuffled", i2, f2, d2])
i2, f2, d2 = test_hash_table(sorted_data)
results.append(["HashTable", "sorted", i2, f2, d2])
# -------- BST --------
i3, f3, d3 = test_bst(shuffled)
results.append(["BST", "shuffled", i3, f3, d3])
i3, f3, d3 = test_bst(sorted_data)
results.append(["BST", "sorted", i3, f3, d3])
with open("results.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerows(results)
print("Готово! results.csv создан")
if __name__ == "__main__":
run()

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View File

@ -0,0 +1,51 @@
import csv
import matplotlib.pyplot as plt
from collections import defaultdict
data = []
with open("results.csv", "r") as f:
reader = csv.reader(f)
next(reader) # header
for row in reader:
structure, order, insert, find, delete = row
data.append((structure, order, float(insert), float(find), float(delete)))
def group(metric_index):
result = defaultdict(list)
for s, o, ins, f, d in data:
key = (s, o)
result[key].append([ins, f, d][metric_index])
return result
def save_plot(metric_name, index, filename):
grouped = group(index)
plt.figure()
labels = []
for (structure, order), values in grouped.items():
label = f"{structure}-{order}"
labels.append(label)
plt.plot(values, label=label)
plt.title(metric_name)
plt.xlabel("Run")
plt.ylabel("Time (sec)")
plt.legend()
plt.savefig(filename, dpi=300, bbox_inches="tight")
plt.close()
save_plot("INSERT TIME", 0, "insert.png")
save_plot("FIND TIME", 1, "find.png")
save_plot("DELETE TIME", 2, "delete.png")
print("Графики сохранены: insert.png, find.png, delete.png")

View File

@ -0,0 +1,31 @@
Structure,Order,Insert,Find,Delete
LinkedList,shuffled,3.1266312,0.0240491,0.0153019
LinkedList,sorted,2.1736874,0.0176649,0.0123909
HashTable,shuffled,0.0102941,0.000103,5.22E-05
HashTable,sorted,0.0093038,0.0001008,5.37E-05
BST,shuffled,0.0154676,0.000154,9.03E-05
BST,sorted,8.3014441,0.0768616,0.0345065
LinkedList,shuffled,3.2182401,0.0256622,0.0177542
LinkedList,sorted,2.2604362,0.0199027,0.0131383
HashTable,shuffled,0.0110818,0.0001227,5.32E-05
HashTable,sorted,0.0098066,0.0001021,5.52E-05
BST,shuffled,0.0184675,0.0001531,9.83E-05
BST,sorted,8.6233891,0.0829446,0.0509548
LinkedList,shuffled,3.2111766,0.0260377,0.0221557
LinkedList,sorted,2.6125727,0.0275846,0.0190955
HashTable,shuffled,0.014219,0.0001666,0.0003187
HashTable,sorted,0.0138165,0.0001425,7.94E-05
BST,shuffled,0.0234246,0.0002257,0.0001382
BST,sorted,8.9193103,0.0615153,0.0415436
LinkedList,shuffled,3.4287802,0.0276346,0.0178373
LinkedList,sorted,2.4205873,0.0211635,0.0157121
HashTable,shuffled,0.0113899,0.0001118,5.63E-05
HashTable,sorted,0.0116161,0.0001284,6.88E-05
BST,shuffled,0.0221165,0.0001773,0.0001002
BST,sorted,9.0077053,0.1054247,0.0477643
LinkedList,shuffled,3.2640041,0.0345332,0.0232472
LinkedList,sorted,2.827502,0.0201937,0.0141592
HashTable,shuffled,0.0105407,0.0001501,8.18E-05
HashTable,sorted,0.0102284,0.0001005,5.58E-05
BST,shuffled,0.0166355,0.0001603,9.34E-05
BST,sorted,8.2043019,0.0622669,0.0375809
1 Structure Order Insert Find Delete
2 LinkedList shuffled 3.1266312 0.0240491 0.0153019
3 LinkedList sorted 2.1736874 0.0176649 0.0123909
4 HashTable shuffled 0.0102941 0.000103 5.22E-05
5 HashTable sorted 0.0093038 0.0001008 5.37E-05
6 BST shuffled 0.0154676 0.000154 9.03E-05
7 BST sorted 8.3014441 0.0768616 0.0345065
8 LinkedList shuffled 3.2182401 0.0256622 0.0177542
9 LinkedList sorted 2.2604362 0.0199027 0.0131383
10 HashTable shuffled 0.0110818 0.0001227 5.32E-05
11 HashTable sorted 0.0098066 0.0001021 5.52E-05
12 BST shuffled 0.0184675 0.0001531 9.83E-05
13 BST sorted 8.6233891 0.0829446 0.0509548
14 LinkedList shuffled 3.2111766 0.0260377 0.0221557
15 LinkedList sorted 2.6125727 0.0275846 0.0190955
16 HashTable shuffled 0.014219 0.0001666 0.0003187
17 HashTable sorted 0.0138165 0.0001425 7.94E-05
18 BST shuffled 0.0234246 0.0002257 0.0001382
19 BST sorted 8.9193103 0.0615153 0.0415436
20 LinkedList shuffled 3.4287802 0.0276346 0.0178373
21 LinkedList sorted 2.4205873 0.0211635 0.0157121
22 HashTable shuffled 0.0113899 0.0001118 5.63E-05
23 HashTable sorted 0.0116161 0.0001284 6.88E-05
24 BST shuffled 0.0221165 0.0001773 0.0001002
25 BST sorted 9.0077053 0.1054247 0.0477643
26 LinkedList shuffled 3.2640041 0.0345332 0.0232472
27 LinkedList sorted 2.827502 0.0201937 0.0141592
28 HashTable shuffled 0.0105407 0.0001501 8.18E-05
29 HashTable sorted 0.0102284 0.0001005 5.58E-05
30 BST shuffled 0.0166355 0.0001603 9.34E-05
31 BST sorted 8.2043019 0.0622669 0.0375809

Binary file not shown.

0
pogodinda/427.md.txt Normal file
View File

BIN
raskatovia/429.md Normal file

Binary file not shown.

1
rybakovaa/428b.md Normal file
View File

@ -0,0 +1 @@
428b