forked from UNN/2026-rff_mp
Compare commits
8 Commits
data_struc
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| 82e988c965 | |||
| 58daf860ed | |||
| 566d89fda2 | |||
| c7229154ca | |||
| fe9ce65eb2 | |||
| 3a251f06c7 | |||
|
|
7e84caffc4 | ||
| 84e5d1e763 |
1
MalkinMV/428b.md
Normal file
1
MalkinMV/428b.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
428b
|
||||||
0
dyachenkoas/428
Normal file
0
dyachenkoas/428
Normal file
0
kalinovskiymi/428
Normal file
0
kalinovskiymi/428
Normal file
2
lomakinae/.gitignore
vendored
2
lomakinae/.gitignore
vendored
|
|
@ -1,2 +0,0 @@
|
||||||
__pycache__/
|
|
||||||
*.pyc
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
# Отчёт. Задание 1 - структуры данных
|
|
||||||
|
|
||||||
## Цель
|
|
||||||
|
|
||||||
Реализовать три структуры данных (связный список, хеш-таблица, BST) и экспериментально сравнить их
|
|
||||||
производительность на операциях insert / find / delete при случайном и отсортированном порядке входных
|
|
||||||
данных.
|
|
||||||
|
|
||||||
## Параметры эксперимента
|
|
||||||
|
|
||||||
| Параметр | Значение |
|
|
||||||
| ----------- | ------------------------------------ |
|
|
||||||
| N (записей) | 10 000 |
|
|
||||||
| Повторений | 5 |
|
|
||||||
| Поисков | 100 существующих + 10 несуществующих |
|
|
||||||
| Удалений | 50 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Результаты
|
|
||||||
|
|
||||||
### Случайные данные (shuffled)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Отсортированные данные (sorted)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Анализ
|
|
||||||
|
|
||||||
### Деградация BST на отсортированных данных
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
На случайных данных BST - самая быстрая структура: insert 0.027 с. Случайный порядок вставки даёт сбалансированное
|
|
||||||
дерево глубиной ~log N, поэтому каждый новый узел находит своё место за O(log N) шагов. На отсортированных - 12.77 с,
|
|
||||||
то есть в ~473 раз медленнее. Причина: при последовательной вставке отсортированных ключей каждый новый узел уходит в
|
|
||||||
правое поддерево предыдущего. Дерево вырождается в цепочку глубиной N, и каждая вставка требует O(N) шагов вместо O(log N).
|
|
||||||
|
|
||||||
### Хеш-таблица нечувствительна к порядку
|
|
||||||
|
|
||||||
HashTable показывает практически одинаковое время в обоих режимах (insert: 0.033 с против 0.032 с). Это ожидаемо:
|
|
||||||
индекс бакета вычисляется через `hash(name)`, который не зависит от порядка вставки. Операции работают за O(1)
|
|
||||||
при любом входе.
|
|
||||||
|
|
||||||
### Связный список медленен при поиске
|
|
||||||
|
|
||||||
LinkedList не имеет никакой структуры для навигации - единственный способ найти запись это пройти список от головы до нужного узла.
|
|
||||||
Find всегда O(N) независимо от порядка данных: shuffled 0.041 с, sorted 0.039 с. Insert O(N^2) для всей выборки - перед каждой вставкой
|
|
||||||
нужно пройти весь список для проверки дубликата. На отсортированных данных LinkedList не меняет поведение, тогда как BST деградирует
|
|
||||||
до 12.77 с - в этом единственном сценарии LinkedList оказывается быстрее BST.
|
|
||||||
|
|
||||||
### Удаление
|
|
||||||
|
|
||||||
LinkedList - чтобы удалить узел, нужно пройти список от головы до нужного элемента и перешить next предшественника.
|
|
||||||
Это O(N) в любом случае, порядок данных не имеет значения. Shuffled: 0.027 с, sorted: 0.026 с - разница в пределах погрешности.
|
|
||||||
|
|
||||||
HashTable - вычисляем индекс бакета через `hash(name)`, затем удаляем узел из связного списка этого бакета. Порядок вставки не
|
|
||||||
влияет на то, в каком бакете лежит запись, поэтому время стабильно в обоих режимах: 0.00033 с.
|
|
||||||
|
|
||||||
BST - ищем узел спуском по дереву, затем обрабатываем три случая: нет потомков, один потомок, два потомка. На случайных данных
|
|
||||||
дерево сбалансировано, глубина ~log N, удаление занимает 0.00014 с. На отсортированных данных дерево вырождено в
|
|
||||||
цепочку - каждый узел уходил в правое поддерево при вставке, поэтому поиск удаляемого узла проходит через всю цепочку O(N).
|
|
||||||
Результат: 0.061 с, то есть в ~435 раз медленнее.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Вывод
|
|
||||||
|
|
||||||
**Частые вставки** - HashTable. Время вставки не зависит от порядка и объёма данных: индекс бакета вычисляется за O(1),
|
|
||||||
вставка в бакет - тоже O(1). Подтверждают цифры: 0.033 с на shuffled и 0.032 с на sorted при N=10000.
|
|
||||||
|
|
||||||
**Частый поиск** - HashTable. По той же причине: `hash(name)` сразу указывает на нужный бакет, линейный перебор не нужен.
|
|
||||||
Find: 0.00057 с на shuffled, 0.00070 с на sorted - стабильно при любом входе.
|
|
||||||
|
|
||||||
**Получить данные в отсортированном порядке** - BST при случайном порядке вставки. Элементы размещаются по правилу BST
|
|
||||||
(слева меньшие корня, справа большие корня), поэтому обход по схеме левое поддерево -> корень -> правое поддерево возвращает
|
|
||||||
все записи в алфавитном порядке без дополнительной сортировки. Важное условие: данные должны вставляться в случайном
|
|
||||||
порядке, иначе дерево вырождается (см. деградацию BST на отсортированных данных).
|
|
||||||
|
|
||||||
LinkedList сам по себе проигрывает по всем операциям из-за O(N\*\*2) на вставку и O(N) на поиск. Однако его идея лежит в основе
|
|
||||||
хеш-таблицы: каждый бакет - это связный список, через который разрешаются коллизии. Как самостоятельная структура данных для
|
|
||||||
справочника он неэффективен, но как строительный блок внутри HashTable - незаменим.
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
# Задание 1: структуры данных
|
|
||||||
|
|
||||||
## Как запустить
|
|
||||||
```sh
|
|
||||||
python main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## Результаты
|
|
||||||
Графики генерируются автоматически в папку `attachments/`.
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 66 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 128 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 132 KiB |
|
|
@ -1,6 +0,0 @@
|
||||||
from src.experiment import main_experiment
|
|
||||||
from src.plot import build_plots
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main_experiment()
|
|
||||||
build_plots()
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
structure,mode,operation,run,time_sec
|
|
||||||
LinkedList,shuffled,insert,1,3.25562
|
|
||||||
LinkedList,shuffled,find,1,0.040773
|
|
||||||
LinkedList,shuffled,delete,1,0.026344
|
|
||||||
HashTable,shuffled,insert,1,0.033497
|
|
||||||
HashTable,shuffled,find,1,0.000593
|
|
||||||
HashTable,shuffled,delete,1,0.000348
|
|
||||||
BST,shuffled,insert,1,0.024071
|
|
||||||
BST,shuffled,find,1,0.000218
|
|
||||||
BST,shuffled,delete,1,0.000136
|
|
||||||
LinkedList,shuffled,insert,2,3.454281
|
|
||||||
LinkedList,shuffled,find,2,0.040282
|
|
||||||
LinkedList,shuffled,delete,2,0.026526
|
|
||||||
HashTable,shuffled,insert,2,0.031691
|
|
||||||
HashTable,shuffled,find,2,0.000568
|
|
||||||
HashTable,shuffled,delete,2,0.000338
|
|
||||||
BST,shuffled,insert,2,0.024978
|
|
||||||
BST,shuffled,find,2,0.000213
|
|
||||||
BST,shuffled,delete,2,0.000135
|
|
||||||
LinkedList,shuffled,insert,3,3.453681
|
|
||||||
LinkedList,shuffled,find,3,0.0404
|
|
||||||
LinkedList,shuffled,delete,3,0.026843
|
|
||||||
HashTable,shuffled,insert,3,0.031902
|
|
||||||
HashTable,shuffled,find,3,0.000536
|
|
||||||
HashTable,shuffled,delete,3,0.000319
|
|
||||||
BST,shuffled,insert,3,0.025369
|
|
||||||
BST,shuffled,find,3,0.000219
|
|
||||||
BST,shuffled,delete,3,0.000138
|
|
||||||
LinkedList,shuffled,insert,4,3.417185
|
|
||||||
LinkedList,shuffled,find,4,0.040816
|
|
||||||
LinkedList,shuffled,delete,4,0.027023
|
|
||||||
HashTable,shuffled,insert,4,0.037826
|
|
||||||
HashTable,shuffled,find,4,0.000582
|
|
||||||
HashTable,shuffled,delete,4,0.00033
|
|
||||||
BST,shuffled,insert,4,0.036423
|
|
||||||
BST,shuffled,find,4,0.000227
|
|
||||||
BST,shuffled,delete,4,0.00014
|
|
||||||
LinkedList,shuffled,insert,5,3.4723
|
|
||||||
LinkedList,shuffled,find,5,0.040734
|
|
||||||
LinkedList,shuffled,delete,5,0.027866
|
|
||||||
HashTable,shuffled,insert,5,0.031981
|
|
||||||
HashTable,shuffled,find,5,0.000546
|
|
||||||
HashTable,shuffled,delete,5,0.000332
|
|
||||||
BST,shuffled,insert,5,0.024578
|
|
||||||
BST,shuffled,find,5,0.000227
|
|
||||||
BST,shuffled,delete,5,0.000146
|
|
||||||
LinkedList,sorted,insert,1,3.271489
|
|
||||||
LinkedList,sorted,find,1,0.038886
|
|
||||||
LinkedList,sorted,delete,1,0.026646
|
|
||||||
HashTable,sorted,insert,1,0.030995
|
|
||||||
HashTable,sorted,find,1,0.000625
|
|
||||||
HashTable,sorted,delete,1,0.000302
|
|
||||||
BST,sorted,insert,1,13.000812
|
|
||||||
BST,sorted,find,1,0.128239
|
|
||||||
BST,sorted,delete,1,0.06369
|
|
||||||
LinkedList,sorted,insert,2,3.384572
|
|
||||||
LinkedList,sorted,find,2,0.03915
|
|
||||||
LinkedList,sorted,delete,2,0.026683
|
|
||||||
HashTable,sorted,insert,2,0.032596
|
|
||||||
HashTable,sorted,find,2,0.0006
|
|
||||||
HashTable,sorted,delete,2,0.000315
|
|
||||||
BST,sorted,insert,2,12.593249
|
|
||||||
BST,sorted,find,2,0.10657
|
|
||||||
BST,sorted,delete,2,0.058763
|
|
||||||
LinkedList,sorted,insert,3,3.27816
|
|
||||||
LinkedList,sorted,find,3,0.038938
|
|
||||||
LinkedList,sorted,delete,3,0.025567
|
|
||||||
HashTable,sorted,insert,3,0.03168
|
|
||||||
HashTable,sorted,find,3,0.000631
|
|
||||||
HashTable,sorted,delete,3,0.00031
|
|
||||||
BST,sorted,insert,3,12.809241
|
|
||||||
BST,sorted,find,3,0.110947
|
|
||||||
BST,sorted,delete,3,0.062604
|
|
||||||
LinkedList,sorted,insert,4,3.277437
|
|
||||||
LinkedList,sorted,find,4,0.039812
|
|
||||||
LinkedList,sorted,delete,4,0.025627
|
|
||||||
HashTable,sorted,insert,4,0.031844
|
|
||||||
HashTable,sorted,find,4,0.000917
|
|
||||||
HashTable,sorted,delete,4,0.000383
|
|
||||||
BST,sorted,insert,4,12.722063
|
|
||||||
BST,sorted,find,4,0.111841
|
|
||||||
BST,sorted,delete,4,0.060014
|
|
||||||
LinkedList,sorted,insert,5,3.261706
|
|
||||||
LinkedList,sorted,find,5,0.037981
|
|
||||||
LinkedList,sorted,delete,5,0.025241
|
|
||||||
HashTable,sorted,insert,5,0.032067
|
|
||||||
HashTable,sorted,find,5,0.000742
|
|
||||||
HashTable,sorted,delete,5,0.000342
|
|
||||||
BST,sorted,insert,5,12.713176
|
|
||||||
BST,sorted,find,5,0.108333
|
|
||||||
BST,sorted,delete,5,0.059109
|
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
import time
|
|
||||||
from .ll import ll_insert, ll_find, ll_delete
|
|
||||||
from .ht import ht_new, ht_insert, ht_find, ht_delete
|
|
||||||
from .bst import bst_insert, bst_find, bst_delete
|
|
||||||
|
|
||||||
|
|
||||||
def _build_ll(records):
|
|
||||||
head = None
|
|
||||||
for name, phone in records:
|
|
||||||
head = ll_insert(head, name, phone)
|
|
||||||
return head
|
|
||||||
|
|
||||||
|
|
||||||
def _build_ht(records):
|
|
||||||
buckets = ht_new()
|
|
||||||
for name, phone in records:
|
|
||||||
ht_insert(buckets, name, phone)
|
|
||||||
return buckets
|
|
||||||
|
|
||||||
|
|
||||||
def _build_bst(records):
|
|
||||||
root = None
|
|
||||||
for name, phone in records:
|
|
||||||
root = bst_insert(root, name, phone)
|
|
||||||
return root
|
|
||||||
|
|
||||||
|
|
||||||
def _time_insert(build_fn, records):
|
|
||||||
start = time.perf_counter()
|
|
||||||
structure = build_fn(records)
|
|
||||||
end = time.perf_counter()
|
|
||||||
elapsed = end - start
|
|
||||||
return elapsed, structure
|
|
||||||
|
|
||||||
|
|
||||||
def _time_find(find_fn, structure, names):
|
|
||||||
start = time.perf_counter()
|
|
||||||
for name in names:
|
|
||||||
find_fn(structure, name)
|
|
||||||
end = time.perf_counter()
|
|
||||||
elapsed = end - start
|
|
||||||
return elapsed
|
|
||||||
|
|
||||||
|
|
||||||
def _time_delete(delete_fn, structure, names):
|
|
||||||
start = time.perf_counter()
|
|
||||||
for name in names:
|
|
||||||
result = delete_fn(structure, name)
|
|
||||||
if result is not None:
|
|
||||||
structure = result
|
|
||||||
end = time.perf_counter()
|
|
||||||
elapsed = end - start
|
|
||||||
return elapsed, structure
|
|
||||||
|
|
||||||
|
|
||||||
def run_once(records, search_names, delete_names):
|
|
||||||
results = []
|
|
||||||
|
|
||||||
structures = {
|
|
||||||
'LinkedList': (_build_ll, ll_find, ll_delete),
|
|
||||||
'HashTable': (_build_ht, ht_find, ht_delete),
|
|
||||||
'BST': (_build_bst, bst_find, bst_delete),
|
|
||||||
}
|
|
||||||
|
|
||||||
for label, (build_fn, find_fn, delete_fn) in structures.items():
|
|
||||||
t_insert, structure = _time_insert(build_fn, records)
|
|
||||||
t_find = _time_find(find_fn, structure, search_names)
|
|
||||||
t_delete, structure = _time_delete(delete_fn, structure, delete_names)
|
|
||||||
|
|
||||||
results.append((label, t_insert, t_find, t_delete))
|
|
||||||
|
|
||||||
return results
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
def _bst_new_node(name, phone):
|
|
||||||
return {'name': name, 'phone': phone, 'left': None, 'right': None}
|
|
||||||
|
|
||||||
|
|
||||||
def bst_insert(root, name, phone):
|
|
||||||
if root is None:
|
|
||||||
return _bst_new_node(name, phone)
|
|
||||||
|
|
||||||
if name == root['name']:
|
|
||||||
root['phone'] = phone
|
|
||||||
elif name < root['name']:
|
|
||||||
root['left'] = bst_insert(root['left'], name, phone)
|
|
||||||
else:
|
|
||||||
root['right'] = bst_insert(root['right'], name, phone)
|
|
||||||
|
|
||||||
return root
|
|
||||||
|
|
||||||
|
|
||||||
def bst_find(root, name):
|
|
||||||
if root is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if name == root['name']:
|
|
||||||
return root['phone']
|
|
||||||
elif name < root['name']:
|
|
||||||
return bst_find(root['left'], name)
|
|
||||||
else:
|
|
||||||
return bst_find(root['right'], name)
|
|
||||||
|
|
||||||
|
|
||||||
def _bst_min_node(root):
|
|
||||||
node = root
|
|
||||||
while node['left'] is not None:
|
|
||||||
node = node['left']
|
|
||||||
return node
|
|
||||||
|
|
||||||
|
|
||||||
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:
|
|
||||||
# node found — three cases
|
|
||||||
if root['left'] is None:
|
|
||||||
return root['right']
|
|
||||||
if root['right'] is None:
|
|
||||||
return root['left']
|
|
||||||
|
|
||||||
# two children: replace node with in-order successor
|
|
||||||
successor = _bst_min_node(root['right'])
|
|
||||||
root['name'] = successor['name']
|
|
||||||
root['phone'] = successor['phone']
|
|
||||||
root['right'] = bst_delete(root['right'], successor['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'])
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
import csv
|
|
||||||
import random
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from .generator import generate_records, shuffle_records, sort_records, sample_existing, sample_nonexistent
|
|
||||||
from .bench import run_once
|
|
||||||
|
|
||||||
N = 10000
|
|
||||||
RUNS = 5
|
|
||||||
SEARCH_K = 100
|
|
||||||
SEARCH_MISSING_K = 10
|
|
||||||
DELETE_K = 50
|
|
||||||
sys.setrecursionlimit(15000)
|
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
||||||
RESULT_PATH = BASE_DIR / "results.csv"
|
|
||||||
|
|
||||||
def run_experiment(records, mode):
|
|
||||||
search_names = sample_existing(records, SEARCH_K) + sample_nonexistent(SEARCH_MISSING_K)
|
|
||||||
delete_names = sample_existing(records, DELETE_K)
|
|
||||||
|
|
||||||
all_rows = []
|
|
||||||
|
|
||||||
for run_i in range(1, RUNS + 1):
|
|
||||||
print(f" [{mode}] run {run_i}/{RUNS} ...")
|
|
||||||
run_results = run_once(records, search_names, delete_names)
|
|
||||||
for label, t_insert, t_find, t_delete in run_results:
|
|
||||||
all_rows.append([label, mode, 'insert', run_i, round(t_insert, 6)])
|
|
||||||
all_rows.append([label, mode, 'find', run_i, round(t_find, 6)])
|
|
||||||
all_rows.append([label, mode, 'delete', run_i, round(t_delete, 6)])
|
|
||||||
|
|
||||||
return all_rows
|
|
||||||
|
|
||||||
|
|
||||||
def main_experiment():
|
|
||||||
random.seed(52)
|
|
||||||
|
|
||||||
records_base = generate_records(N)
|
|
||||||
records_shuffled = shuffle_records(records_base)
|
|
||||||
records_sorted = sort_records(records_base)
|
|
||||||
|
|
||||||
rows = []
|
|
||||||
rows += run_experiment(records_shuffled, 'shuffled')
|
|
||||||
rows += run_experiment(records_sorted, 'sorted')
|
|
||||||
|
|
||||||
header = ['structure', 'mode', 'operation', 'run', 'time_sec']
|
|
||||||
output_path = RESULT_PATH
|
|
||||||
|
|
||||||
with open(output_path, 'w', newline='') as f:
|
|
||||||
writer = csv.writer(f)
|
|
||||||
writer.writerow(header)
|
|
||||||
writer.writerows(rows)
|
|
||||||
|
|
||||||
print(f"Done. Results saved to {output_path.name}")
|
|
||||||
print(f"Total rows: {len(rows)}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main_experiment()
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
import random
|
|
||||||
|
|
||||||
|
|
||||||
def generate_records(n):
|
|
||||||
records = [(f"User_{i:05d}", f"252-{i:05d}") for i in range(n)]
|
|
||||||
return records
|
|
||||||
|
|
||||||
|
|
||||||
def shuffle_records(records):
|
|
||||||
shuffled = records[:]
|
|
||||||
random.shuffle(shuffled)
|
|
||||||
return shuffled
|
|
||||||
|
|
||||||
|
|
||||||
def sort_records(records):
|
|
||||||
sorted_records = sorted(records)
|
|
||||||
return sorted_records
|
|
||||||
|
|
||||||
|
|
||||||
def sample_existing(records, k):
|
|
||||||
names = [name for name, _ in random.sample(records, k)]
|
|
||||||
return names
|
|
||||||
|
|
||||||
|
|
||||||
def sample_nonexistent(k):
|
|
||||||
ghosts = [f"None_{i:05d}" for i in range(k)]
|
|
||||||
return ghosts
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
from .ll import ll_insert, ll_find, ll_delete, ll_list_all
|
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_SIZE = 128
|
|
||||||
|
|
||||||
|
|
||||||
def ht_new(size=DEFAULT_SIZE):
|
|
||||||
return [None] * size
|
|
||||||
|
|
||||||
|
|
||||||
def _ht_index(buckets, name):
|
|
||||||
return hash(name) % len(buckets)
|
|
||||||
|
|
||||||
|
|
||||||
def ht_insert(buckets, name, phone):
|
|
||||||
i = _ht_index(buckets, name)
|
|
||||||
buckets[i] = ll_insert(buckets[i], name, phone)
|
|
||||||
|
|
||||||
|
|
||||||
def ht_find(buckets, name):
|
|
||||||
i = _ht_index(buckets, name)
|
|
||||||
return ll_find(buckets[i], name)
|
|
||||||
|
|
||||||
|
|
||||||
def ht_delete(buckets, name):
|
|
||||||
i = _ht_index(buckets, name)
|
|
||||||
buckets[i] = ll_delete(buckets[i], name)
|
|
||||||
|
|
||||||
|
|
||||||
def ht_list_all(buckets):
|
|
||||||
records = []
|
|
||||||
for head in buckets:
|
|
||||||
records.extend(ll_list_all(head))
|
|
||||||
return sorted(records)
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
def _ll_new_node(name, phone):
|
|
||||||
return {'name': name, 'phone': phone, 'next': None}
|
|
||||||
|
|
||||||
|
|
||||||
def ll_insert(head, name, phone):
|
|
||||||
node = head
|
|
||||||
while node is not None:
|
|
||||||
if node['name'] == name:
|
|
||||||
node['phone'] = phone
|
|
||||||
return head
|
|
||||||
node = node['next']
|
|
||||||
|
|
||||||
new_node = _ll_new_node(name, phone)
|
|
||||||
new_node['next'] = head
|
|
||||||
return new_node
|
|
||||||
|
|
||||||
|
|
||||||
def ll_find(head, name):
|
|
||||||
node = head
|
|
||||||
while node is not None:
|
|
||||||
if node['name'] == name:
|
|
||||||
return node['phone']
|
|
||||||
node = node['next']
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def ll_delete(head, name):
|
|
||||||
if head is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if head['name'] == name:
|
|
||||||
return head['next']
|
|
||||||
|
|
||||||
node = head
|
|
||||||
while node['next'] is not None:
|
|
||||||
if node['next']['name'] == name:
|
|
||||||
node['next'] = node['next']['next']
|
|
||||||
return head
|
|
||||||
node = node['next']
|
|
||||||
|
|
||||||
return head
|
|
||||||
|
|
||||||
|
|
||||||
def ll_list_all(head):
|
|
||||||
records = []
|
|
||||||
node = head
|
|
||||||
while node is not None:
|
|
||||||
records.append((node['name'], node['phone']))
|
|
||||||
node = node['next']
|
|
||||||
return sorted(records)
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
import pandas as pd
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
||||||
DATA_PATH = BASE_DIR / "results.csv"
|
|
||||||
ATTACHMENTS_DIR = BASE_DIR / "attachments"
|
|
||||||
|
|
||||||
|
|
||||||
def shuffled_sorted_plots(structures, df):
|
|
||||||
for mode in ['shuffled', 'sorted']:
|
|
||||||
mode_title = 'перемешанные данные' if mode == 'shuffled' else 'отсортированные данные'
|
|
||||||
fig, axes = plt.subplots(1, 3, figsize=(15, 6))
|
|
||||||
fig.suptitle(f'Производительность — {mode_title}')
|
|
||||||
|
|
||||||
for ax, op in zip(axes, ['insert', 'find', 'delete']):
|
|
||||||
subset = df[(df['mode'] == mode) & (df['operation'] == op)]
|
|
||||||
structures_average = subset.groupby('structure')['time_sec'].mean()
|
|
||||||
means = [structures_average[s] for s in structures]
|
|
||||||
|
|
||||||
bars = ax.bar(structures, means, color=['#4C72B0', '#55A868', '#C44E52'])
|
|
||||||
ax.set_title(op)
|
|
||||||
ax.set_ylabel('t (с)')
|
|
||||||
ax.set_yscale('log')
|
|
||||||
ax.grid(True, axis='y', alpha=0.3)
|
|
||||||
|
|
||||||
for bar, val in zip(bars, means):
|
|
||||||
label = f'{val:.5f}' if val > 0.0001 else f'{val:.1e}'
|
|
||||||
ax.text(bar.get_x() + bar.get_width() / 2, bar.get_height(),
|
|
||||||
label, ha='center', va='bottom', fontsize=9)
|
|
||||||
|
|
||||||
plt.tight_layout()
|
|
||||||
save_path = ATTACHMENTS_DIR / f'plot_{mode}.png'
|
|
||||||
plt.savefig(save_path, dpi=300)
|
|
||||||
plt.close()
|
|
||||||
print(f'Saved: {save_path.name}')
|
|
||||||
|
|
||||||
|
|
||||||
def bst_shuffled_vs_sorted(df):
|
|
||||||
fig, ax = plt.subplots(figsize=(7, 5))
|
|
||||||
fig.suptitle('Производительность bst_insert(): перемешанные и упорядоченные данные')
|
|
||||||
|
|
||||||
bst_insert = df[(df['structure'] == 'BST') & (df['operation'] == 'insert')]
|
|
||||||
modes_average = bst_insert.groupby('mode')['time_sec'].mean()
|
|
||||||
modes = ['shuffled', 'sorted']
|
|
||||||
means = [modes_average[m] for m in modes]
|
|
||||||
|
|
||||||
bars = ax.bar(modes, means, color=['#4C72B0', '#C44E52'])
|
|
||||||
ax.set_ylabel('t (c)')
|
|
||||||
ax.set_yscale('log')
|
|
||||||
ax.grid(True, axis='y', alpha=0.3)
|
|
||||||
|
|
||||||
for bar, val in zip(bars, means):
|
|
||||||
label = f'{val:.5f}' if val > 0.0001 else f'{val:.1e}'
|
|
||||||
ax.text(bar.get_x() + bar.get_width() / 2, bar.get_height(),
|
|
||||||
label, ha='center', va='bottom', fontsize=10)
|
|
||||||
|
|
||||||
plt.tight_layout()
|
|
||||||
save_path = ATTACHMENTS_DIR / 'plot_bst_comparison.png'
|
|
||||||
plt.savefig(save_path, dpi=300)
|
|
||||||
plt.close()
|
|
||||||
print(f'Saved: {save_path.name}')
|
|
||||||
|
|
||||||
|
|
||||||
def build_plots():
|
|
||||||
ATTACHMENTS_DIR.mkdir(exist_ok=True)
|
|
||||||
|
|
||||||
if not DATA_PATH.exists():
|
|
||||||
raise ValueError(f"File not found: {DATA_PATH}")
|
|
||||||
|
|
||||||
df = pd.read_csv(DATA_PATH)
|
|
||||||
|
|
||||||
STRUCTURES = ['LinkedList', 'HashTable', 'BST']
|
|
||||||
|
|
||||||
shuffled_sorted_plots(STRUCTURES, df)
|
|
||||||
bst_shuffled_vs_sorted(df)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
build_plots()
|
|
||||||
Loading…
Reference in New Issue
Block a user