forked from UNN/2026-rff_mp
Compare commits
7 Commits
82e988c965
...
a9deaa51ec
| Author | SHA1 | Date | |
|---|---|---|---|
| a9deaa51ec | |||
| 51f77e7a5a | |||
| d5b077f8dd | |||
| bf2dc50479 | |||
| 77485a5318 | |||
| fb36061d9a | |||
| 72d2269634 |
305
soninrv/docs/data/lab1/phonebook.py
Normal file
305
soninrv/docs/data/lab1/phonebook.py
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
# 1. СВЯЗНЫЙ СПИСОК
|
||||
def ll_create_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']
|
||||
# Вставка в начало — O(1)
|
||||
new_node = ll_create_node(name, phone)
|
||||
new_node['next'] = head
|
||||
return new_node
|
||||
|
||||
|
||||
def ll_find(head, name):
|
||||
"""Вернуть телефон или None."""
|
||||
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']
|
||||
prev, node = head, head['next']
|
||||
while node is not None:
|
||||
if node['name'] == name:
|
||||
prev['next'] = node['next']
|
||||
return head
|
||||
prev, node = node, node['next']
|
||||
return head
|
||||
|
||||
|
||||
def ll_list_all(head):
|
||||
"""Собрать все записи и вернуть отсортированный список (name, phone)."""
|
||||
result = []
|
||||
node = head
|
||||
while node is not None:
|
||||
result.append((node['name'], node['phone']))
|
||||
node = node['next']
|
||||
result.sort(key=lambda x: x[0])
|
||||
return result
|
||||
|
||||
# 2. ХЕШ-ТАБЛИЦА (цепочки через связный список)
|
||||
|
||||
HT_SIZE = 1024 # число корзин (степень двойки)
|
||||
|
||||
|
||||
def ht_create(size=HT_SIZE):
|
||||
return [None] * size
|
||||
|
||||
|
||||
def _ht_hash(name, size):
|
||||
h = 5381
|
||||
for ch in name:
|
||||
h = ((h << 5) + h) ^ ord(ch)
|
||||
return h % size
|
||||
|
||||
|
||||
def ht_insert(buckets, name, phone):
|
||||
idx = _ht_hash(name, len(buckets))
|
||||
buckets[idx] = ll_insert(buckets[idx], name, phone)
|
||||
|
||||
|
||||
def ht_find(buckets, name):
|
||||
idx = _ht_hash(name, len(buckets))
|
||||
return ll_find(buckets[idx], name)
|
||||
|
||||
|
||||
def ht_delete(buckets, name):
|
||||
idx = _ht_hash(name, len(buckets))
|
||||
buckets[idx] = ll_delete(buckets[idx], name)
|
||||
|
||||
|
||||
def ht_list_all(buckets):
|
||||
result = []
|
||||
for head in buckets:
|
||||
result.extend(ll_list_all(head))
|
||||
result.sort(key=lambda x: x[0])
|
||||
return result
|
||||
|
||||
# 3. ДВОИЧНОЕ ДЕРЕВО ПОИСКА (BST)
|
||||
|
||||
def bst_create_node(name, phone):
|
||||
return {'name': name, 'phone': phone, 'left': None, 'right': None}
|
||||
|
||||
|
||||
def bst_insert(root, name, phone):
|
||||
"""Вставить / обновить. Возвращает корень."""
|
||||
if root is None:
|
||||
return bst_create_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):
|
||||
"""Вернуть телефон или None."""
|
||||
while root is not None:
|
||||
if name == root['name']:
|
||||
return root['phone']
|
||||
elif name < root['name']:
|
||||
root = root['left']
|
||||
else:
|
||||
root = root['right']
|
||||
return None
|
||||
|
||||
|
||||
def _bst_min(node):
|
||||
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:
|
||||
# Узел найден
|
||||
if root['left'] is None:
|
||||
return root['right']
|
||||
if root['right'] is None:
|
||||
return root['left']
|
||||
# Два потомка: заменить минимальным из правого поддерева
|
||||
successor = _bst_min(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):
|
||||
"""Центрированный (in-order) обход → отсортированный список."""
|
||||
result = []
|
||||
stack = []
|
||||
node = root
|
||||
while stack or node is not None:
|
||||
while node is not None:
|
||||
stack.append(node)
|
||||
node = node['left']
|
||||
node = stack.pop()
|
||||
result.append((node['name'], node['phone']))
|
||||
node = node['right']
|
||||
return result
|
||||
|
||||
"""
|
||||
Экспериментальная часть: замер производительности трёх структур данных.
|
||||
"""
|
||||
|
||||
import time
|
||||
import csv
|
||||
import random
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.setrecursionlimit(30000) # BST с отсортированными данными — глубокая рекурсия
|
||||
|
||||
|
||||
# ── Параметры ──────────────────────────────────────────────────────────────
|
||||
N = 10_000 # размер набора
|
||||
REPEATS = 5 # повторений каждого замера
|
||||
SEARCH_N = 100 # запросов на поиск (существующих)
|
||||
SEARCH_MISS = 10 # запросов на поиск (отсутствующих)
|
||||
DELETE_N = 50 # удалений
|
||||
|
||||
random.seed(42)
|
||||
|
||||
# ── Генерация данных ───────────────────────────────────────────────────────
|
||||
records_sorted = [(f"User_{i:05d}", f"+7-000-{i:07d}") for i in range(N)]
|
||||
records_shuffled = records_sorted[:]
|
||||
random.shuffle(records_shuffled)
|
||||
|
||||
search_names_hit = [records_sorted[i][0] for i in random.sample(range(N), SEARCH_N)]
|
||||
search_names_miss = [f"None_{i:04d}" for i in range(SEARCH_MISS)]
|
||||
search_names = search_names_hit + search_names_miss
|
||||
|
||||
delete_names = [records_sorted[i][0] for i in random.sample(range(N), DELETE_N)]
|
||||
|
||||
# ── Вспомогательные функции ────────────────────────────────────────────────
|
||||
|
||||
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_create()
|
||||
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
|
||||
|
||||
STRUCTURES = {
|
||||
'LinkedList': {
|
||||
'build': build_ll,
|
||||
'find': ll_find,
|
||||
'delete': lambda ds, name: ll_delete(ds, name), # возвращает новый head
|
||||
'list_all': ll_list_all,
|
||||
'mutable': False, # ll_delete возвращает новую голову
|
||||
},
|
||||
'HashTable': {
|
||||
'build': build_ht,
|
||||
'find': ht_find,
|
||||
'delete': lambda ds, name: ht_delete(ds, name), # in-place, returns None
|
||||
'list_all': ht_list_all,
|
||||
'mutable': True,
|
||||
},
|
||||
'BST': {
|
||||
'build': build_bst,
|
||||
'find': bst_find,
|
||||
'delete': lambda ds, name: bst_delete(ds, name), # возвращает новый корень
|
||||
'list_all': bst_list_all,
|
||||
'mutable': False,
|
||||
},
|
||||
}
|
||||
|
||||
MODES = {
|
||||
'shuffled': records_shuffled,
|
||||
'sorted': records_sorted,
|
||||
}
|
||||
|
||||
# ── Замер ──────────────────────────────────────────────────────────────────
|
||||
|
||||
def measure(fn, *args, repeats=REPEATS):
|
||||
times = []
|
||||
for _ in range(repeats):
|
||||
t0 = time.perf_counter()
|
||||
fn(*args)
|
||||
times.append(time.perf_counter() - t0)
|
||||
return times
|
||||
|
||||
rows = [["structure", "mode", "operation", "run", "time_sec"]]
|
||||
|
||||
for struct_name, ops in STRUCTURES.items():
|
||||
for mode_name, records in MODES.items():
|
||||
print(f" {struct_name} / {mode_name} ...", flush=True)
|
||||
|
||||
# ── А. Вставка ──────────────────────────────────────────────────
|
||||
insert_times = []
|
||||
for run in range(REPEATS):
|
||||
t0 = time.perf_counter()
|
||||
ds = ops['build'](records)
|
||||
insert_times.append(time.perf_counter() - t0)
|
||||
rows.append([struct_name, mode_name, "insert", run + 1, insert_times[-1]])
|
||||
|
||||
# Строим структуру один раз для поиска и удаления
|
||||
ds = ops['build'](records)
|
||||
|
||||
# ── Б. Поиск ────────────────────────────────────────────────────
|
||||
def do_search(ds=ds):
|
||||
for name in search_names:
|
||||
ops['find'](ds, name)
|
||||
|
||||
search_times = measure(do_search)
|
||||
for run, t in enumerate(search_times, 1):
|
||||
rows.append([struct_name, mode_name, "find", run, t])
|
||||
|
||||
# ── В. Удаление ─────────────────────────────────────────────────
|
||||
# Удаление изменяет структуру, поэтому каждый раз пересобираем
|
||||
delete_times = []
|
||||
for run in range(REPEATS):
|
||||
ds2 = ops['build'](records)
|
||||
t0 = time.perf_counter()
|
||||
for name in delete_names:
|
||||
result = ops['delete'](ds2, name)
|
||||
if result is not None: # ll / bst возвращают новую голову/корень
|
||||
ds2 = result
|
||||
delete_times.append(time.perf_counter() - t0)
|
||||
rows.append([struct_name, mode_name, "delete", run + 1, delete_times[-1]])
|
||||
|
||||
print(f" insert avg={sum(insert_times)/REPEATS:.4f}s "
|
||||
f"find avg={sum(search_times)/REPEATS:.4f}s "
|
||||
f"delete avg={sum(delete_times)/REPEATS:.4f}s")
|
||||
|
||||
|
||||
with open("results.csv", "w", newline="", encoding="utf-8") as f:
|
||||
csv.writer(f).writerows(rows)
|
||||
|
||||
print("\nРезультаты сохранены в docs/data/results.csv")
|
||||
49
soninrv/docs/data/lab1/plots.py
Normal file
49
soninrv/docs/data/lab1/plots.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import csv
|
||||
import statistics
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
rows = []
|
||||
with open("results.csv", encoding="utf-8") as f:
|
||||
for r in csv.DictReader(f):
|
||||
rows.append(r)
|
||||
|
||||
STRUCTS = ["LinkedList", "HashTable", "BST"]
|
||||
MODE_MAP = {"shuffled": "случайный", "sorted": "отсортированный"}
|
||||
OPS = [("insert", "Вставка"), ("find", "Поиск"), ("delete", "Удаление")]
|
||||
|
||||
def stats(structure, mode, operation):
|
||||
vals = [float(r["time_sec"]) for r in rows
|
||||
if r["structure"] == structure and r["mode"] == mode and r["operation"] == operation]
|
||||
if not vals:
|
||||
return 0.0, 0.0
|
||||
return statistics.mean(vals), statistics.stdev(vals) if len(vals) > 1 else 0.0
|
||||
|
||||
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
|
||||
fig.suptitle("Среднее время операций (сек, лог-шкала, N=10 000, 5 повторений)")
|
||||
|
||||
x = np.arange(len(STRUCTS))
|
||||
WIDTH = 0.35
|
||||
|
||||
for ax, (op_key, op_title) in zip(axes, OPS):
|
||||
for i, mode in enumerate(["shuffled", "sorted"]):
|
||||
avgs, stds = [], []
|
||||
for s in STRUCTS:
|
||||
avg, std = stats(s, mode, op_key)
|
||||
avgs.append(avg)
|
||||
stds.append(std)
|
||||
offset = (i - 0.5) * WIDTH
|
||||
ax.bar(x + offset, avgs, WIDTH, label=MODE_MAP[mode])
|
||||
ax.errorbar(x + offset, avgs, yerr=stds, fmt="none", capsize=4)
|
||||
ax.set_yscale("log")
|
||||
ax.set_title(op_title)
|
||||
ax.set_xticks(x)
|
||||
ax.set_xticklabels(STRUCTS)
|
||||
ax.set_ylabel("Время (сек)")
|
||||
ax.yaxis.grid(True, which="both", linestyle="--", alpha=0.5)
|
||||
ax.set_axisbelow(True)
|
||||
ax.legend()
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig("../../performance_comparison.png", dpi=150)
|
||||
plt.show()
|
||||
91
soninrv/docs/data/lab1/results.csv
Normal file
91
soninrv/docs/data/lab1/results.csv
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
structure,mode,operation,run,time_sec
|
||||
LinkedList,shuffled,insert,1,3.294921400000021
|
||||
LinkedList,shuffled,insert,2,2.92912730000171
|
||||
LinkedList,shuffled,insert,3,2.8146583999987342
|
||||
LinkedList,shuffled,insert,4,2.7935691000020597
|
||||
LinkedList,shuffled,insert,5,2.8566659999996773
|
||||
LinkedList,shuffled,find,1,0.03453739999895333
|
||||
LinkedList,shuffled,find,2,0.03489120000085677
|
||||
LinkedList,shuffled,find,3,0.034232199999678414
|
||||
LinkedList,shuffled,find,4,0.03294129999994766
|
||||
LinkedList,shuffled,find,5,0.03249359999972512
|
||||
LinkedList,shuffled,delete,1,0.016195199998037424
|
||||
LinkedList,shuffled,delete,2,0.016463700001622783
|
||||
LinkedList,shuffled,delete,3,0.016346699998393888
|
||||
LinkedList,shuffled,delete,4,0.016296699999656994
|
||||
LinkedList,shuffled,delete,5,0.016424599998572376
|
||||
LinkedList,sorted,insert,1,2.383058199997322
|
||||
LinkedList,sorted,insert,2,2.375423099998443
|
||||
LinkedList,sorted,insert,3,2.34873769999831
|
||||
LinkedList,sorted,insert,4,2.3596142000023974
|
||||
LinkedList,sorted,insert,5,2.3823104000002786
|
||||
LinkedList,sorted,find,1,0.027813299999252195
|
||||
LinkedList,sorted,find,2,0.02766450000126497
|
||||
LinkedList,sorted,find,3,0.027582700000493787
|
||||
LinkedList,sorted,find,4,0.02761159999863594
|
||||
LinkedList,sorted,find,5,0.02766390000033425
|
||||
LinkedList,sorted,delete,1,0.015935499999613967
|
||||
LinkedList,sorted,delete,2,0.01771329999974114
|
||||
LinkedList,sorted,delete,3,0.016032899999117944
|
||||
LinkedList,sorted,delete,4,0.01585219999833498
|
||||
LinkedList,sorted,delete,5,0.016385800001444295
|
||||
HashTable,shuffled,insert,1,0.06008769999971264
|
||||
HashTable,shuffled,insert,2,0.02979799999957322
|
||||
HashTable,shuffled,insert,3,0.02958039999793982
|
||||
HashTable,shuffled,insert,4,0.03261639999982435
|
||||
HashTable,shuffled,insert,5,0.03028959999937797
|
||||
HashTable,shuffled,find,1,0.00040919999810284935
|
||||
HashTable,shuffled,find,2,0.00025829999867710285
|
||||
HashTable,shuffled,find,3,0.000260199998592725
|
||||
HashTable,shuffled,find,4,0.00024839999969117343
|
||||
HashTable,shuffled,find,5,0.0002446999969833996
|
||||
HashTable,shuffled,delete,1,0.0007224000000860542
|
||||
HashTable,shuffled,delete,2,0.00018980000095325522
|
||||
HashTable,shuffled,delete,3,0.00014259999807109125
|
||||
HashTable,shuffled,delete,4,0.00020619999850168824
|
||||
HashTable,shuffled,delete,5,0.00014730000111740083
|
||||
HashTable,sorted,insert,1,0.02703069999915897
|
||||
HashTable,sorted,insert,2,0.0286950000008801
|
||||
HashTable,sorted,insert,3,0.029971800002385862
|
||||
HashTable,sorted,insert,4,0.028408000001945766
|
||||
HashTable,sorted,insert,5,0.028463399998145178
|
||||
HashTable,sorted,find,1,0.00038550000317627564
|
||||
HashTable,sorted,find,2,0.00026449999859323725
|
||||
HashTable,sorted,find,3,0.0002604000001156237
|
||||
HashTable,sorted,find,4,0.0002567999981692992
|
||||
HashTable,sorted,find,5,0.0002595000005385373
|
||||
HashTable,sorted,delete,1,0.00020910000239382498
|
||||
HashTable,sorted,delete,2,0.0002086000022245571
|
||||
HashTable,sorted,delete,3,0.00015020000137155876
|
||||
HashTable,sorted,delete,4,0.0001517000018793624
|
||||
HashTable,sorted,delete,5,0.00015150000035646372
|
||||
BST,shuffled,insert,1,0.026569400000880705
|
||||
BST,shuffled,insert,2,0.028130499998951564
|
||||
BST,shuffled,insert,3,0.02583809999850928
|
||||
BST,shuffled,insert,4,0.02573110000230372
|
||||
BST,shuffled,insert,5,0.02615979999973206
|
||||
BST,shuffled,find,1,0.00020509999740170315
|
||||
BST,shuffled,find,2,0.00017859999934444204
|
||||
BST,shuffled,find,3,0.00017999999909079634
|
||||
BST,shuffled,find,4,0.00017889999799081124
|
||||
BST,shuffled,find,5,0.00017719999959808774
|
||||
BST,shuffled,delete,1,0.00014940000255592167
|
||||
BST,shuffled,delete,2,0.0010156000025745016
|
||||
BST,shuffled,delete,3,0.000994199999695411
|
||||
BST,shuffled,delete,4,0.0011020999991160352
|
||||
BST,shuffled,delete,5,0.0011912000009033363
|
||||
BST,sorted,insert,1,10.031728599999042
|
||||
BST,sorted,insert,2,9.260749099998066
|
||||
BST,sorted,insert,3,9.739691700000549
|
||||
BST,sorted,insert,4,8.961757199998829
|
||||
BST,sorted,insert,5,9.583165900003223
|
||||
BST,sorted,find,1,0.041536599997925805
|
||||
BST,sorted,find,2,0.04151529999944614
|
||||
BST,sorted,find,3,0.04165329999887035
|
||||
BST,sorted,find,4,0.04157439999835333
|
||||
BST,sorted,find,5,0.0415880999971705
|
||||
BST,sorted,delete,1,0.04558349999933853
|
||||
BST,sorted,delete,2,0.041408099998079706
|
||||
BST,sorted,delete,3,0.041001800000231015
|
||||
BST,sorted,delete,4,0.041335800000524614
|
||||
BST,sorted,delete,5,0.041272599999501836
|
||||
|
BIN
soninrv/docs/performance_comparison.png
Normal file
BIN
soninrv/docs/performance_comparison.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
172
soninrv/docs/report1.md
Normal file
172
soninrv/docs/report1.md
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
# Отчёт по лабораторной работе "Структуры данных"
|
||||
|
||||
|
||||
## 1. Цель работы
|
||||
|
||||
Реализовать три структуры данных «с нуля» в процедурной парадигме (без классов), применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций: вставки, поиска и удаления.
|
||||
|
||||
Структуры данных:
|
||||
|
||||
Связный список (LinkedList) — узлы-словари, соединённые ссылками.
|
||||
|
||||
Хеш-таблица (HashTable) — массив корзин (1024 элемента) с цепочками через связный список.
|
||||
|
||||
Двоичное дерево поиска (BST) — рекурсивная / итеративная реализация через словари.
|
||||
|
||||
N = 10 000 записей вида User_00001, +7-000-0000001.
|
||||
|
||||
Два режима: случайный порядок (records_shuffled) и отсортированный (records_sorted).
|
||||
|
||||
Поиск: 100 гарантированно существующих имён + 10 несуществующих = 110 запросов.
|
||||
|
||||
Удаление: 50 случайных имён из набора.
|
||||
|
||||
Каждый замер повторяется 5 раз; записываются все замеры и среднее.
|
||||
|
||||
|
||||
## 2. Результаты экспериментов
|
||||
|
||||
| structure | mode | operation | run | time_sec |
|
||||
|---|---|---|---|---|
|
||||
| LinkedList | shuffled | insert | 1 | 3.688133922999995 |
|
||||
| LinkedList | shuffled | insert | 2 | 3.642716359000005 |
|
||||
| LinkedList | shuffled | insert | 3 | 3.6362029409999934 |
|
||||
| LinkedList | shuffled | insert | 4 | 3.5635424559999933 |
|
||||
| LinkedList | shuffled | insert | 5 | 3.6936824539999975 |
|
||||
| LinkedList | shuffled | find | 1 | 0.0404481799999985 |
|
||||
| LinkedList | shuffled | find | 2 | 0.0415632419999951 |
|
||||
| LinkedList | shuffled | find | 3 | 0.0408364839999961 |
|
||||
| LinkedList | shuffled | find | 4 | 0.0409441910000083 |
|
||||
| LinkedList | shuffled | find | 5 | 0.0409490519999877 |
|
||||
| LinkedList | shuffled | delete | 1 | 0.020429828999994 |
|
||||
| LinkedList | shuffled | delete | 2 | 0.0203125029999995 |
|
||||
| LinkedList | shuffled | delete | 3 | 0.0205162980000039 |
|
||||
| LinkedList | shuffled | delete | 4 | 0.0204522580000059 |
|
||||
| LinkedList | shuffled | delete | 5 | 0.0204940820000132 |
|
||||
| LinkedList | sorted | insert | 1 | 2.807388945 |
|
||||
| LinkedList | sorted | insert | 2 | 2.6681887550000027 |
|
||||
| LinkedList | sorted | insert | 3 | 2.7149360570000027 |
|
||||
| LinkedList | sorted | insert | 4 | 2.586755936000003 |
|
||||
| LinkedList | sorted | insert | 5 | 2.858489943000009 |
|
||||
| LinkedList | sorted | find | 1 | 0.0301240860000007 |
|
||||
| LinkedList | sorted | find | 2 | 0.0300124050000079 |
|
||||
| LinkedList | sorted | find | 3 | 0.0301267250000023 |
|
||||
| LinkedList | sorted | find | 4 | 0.0300742670000033 |
|
||||
| LinkedList | sorted | find | 5 | 0.0304795409999769 |
|
||||
| LinkedList | sorted | delete | 1 | 0.0176948809999828 |
|
||||
| LinkedList | sorted | delete | 2 | 0.0186108259999855 |
|
||||
| LinkedList | sorted | delete | 3 | 0.0183917109999924 |
|
||||
| LinkedList | sorted | delete | 4 | 0.0183299800000042 |
|
||||
| LinkedList | sorted | delete | 5 | 0.0202586389999908 |
|
||||
| HashTable | shuffled | insert | 1 | 0.040671551999992 |
|
||||
| HashTable | shuffled | insert | 2 | 0.0356988590000071 |
|
||||
| HashTable | shuffled | insert | 3 | 0.034698187999993 |
|
||||
| HashTable | shuffled | insert | 4 | 0.034897758999989 |
|
||||
| HashTable | shuffled | insert | 5 | 0.0436747020000041 |
|
||||
| HashTable | shuffled | find | 1 | 0.0003306420000228 |
|
||||
| HashTable | shuffled | find | 2 | 0.0002776770000139 |
|
||||
| HashTable | shuffled | find | 3 | 0.0002387590000125 |
|
||||
| HashTable | shuffled | find | 4 | 0.0002413439999884 |
|
||||
| HashTable | shuffled | find | 5 | 0.0002350800000101 |
|
||||
| HashTable | shuffled | delete | 1 | 0.0009653390000039 |
|
||||
| HashTable | shuffled | delete | 2 | 0.000182843999994 |
|
||||
| HashTable | shuffled | delete | 3 | 0.000187277000009 |
|
||||
| HashTable | shuffled | delete | 4 | 0.0001825169999847 |
|
||||
| HashTable | shuffled | delete | 5 | 0.000182102999986 |
|
||||
| HashTable | sorted | insert | 1 | 0.031514957000013 |
|
||||
| HashTable | sorted | insert | 2 | 0.0317737780000015 |
|
||||
| HashTable | sorted | insert | 3 | 0.0332209919999968 |
|
||||
| HashTable | sorted | insert | 4 | 0.0438333349999879 |
|
||||
| HashTable | sorted | insert | 5 | 0.0344081210000126 |
|
||||
| HashTable | sorted | find | 1 | 0.0004218560000026 |
|
||||
| HashTable | sorted | find | 2 | 0.0003256969999938 |
|
||||
| HashTable | sorted | find | 3 | 0.0003048350000085 |
|
||||
| HashTable | sorted | find | 4 | 0.000252023999991 |
|
||||
| HashTable | sorted | find | 5 | 0.0002450770000166 |
|
||||
| HashTable | sorted | delete | 1 | 0.0002077629999917 |
|
||||
| HashTable | sorted | delete | 2 | 0.000197111999995 |
|
||||
| HashTable | sorted | delete | 3 | 0.000204272000019 |
|
||||
| HashTable | sorted | delete | 4 | 0.0001966060000029 |
|
||||
| HashTable | sorted | delete | 5 | 0.0001917250000076 |
|
||||
| BST | shuffled | insert | 1 | 0.0322367580000104 |
|
||||
| BST | shuffled | insert | 2 | 0.0445325409999952 |
|
||||
| BST | shuffled | insert | 3 | 0.0312052750000191 |
|
||||
| BST | shuffled | insert | 4 | 0.0302206560000115 |
|
||||
| BST | shuffled | insert | 5 | 0.0304544809999924 |
|
||||
| BST | shuffled | find | 1 | 0.000256859999979 |
|
||||
| BST | shuffled | find | 2 | 0.0001786029999948 |
|
||||
| BST | shuffled | find | 3 | 0.0001869349999878 |
|
||||
| BST | shuffled | find | 4 | 0.0001727730000027 |
|
||||
| BST | shuffled | find | 5 | 0.0001574610000147 |
|
||||
| BST | shuffled | delete | 1 | 0.0001869909999925 |
|
||||
| BST | shuffled | delete | 2 | 0.0012688459999878 |
|
||||
| BST | shuffled | delete | 3 | 0.0012691000000017 |
|
||||
| BST | shuffled | delete | 4 | 0.001258899999982 |
|
||||
| BST | shuffled | delete | 5 | 0.0013220630000034 |
|
||||
| BST | sorted | insert | 1 | 12.957382101000007 |
|
||||
| BST | sorted | insert | 2 | 12.10390555699999 |
|
||||
| BST | sorted | insert | 3 | 12.698454105999986 |
|
||||
| BST | sorted | insert | 4 | 12.181134653000017 |
|
||||
| BST | sorted | insert | 5 | 12.952122806999997 |
|
||||
| BST | sorted | find | 1 | 0.0432625550000125 |
|
||||
| BST | sorted | find | 2 | 0.0455909260000169 |
|
||||
| BST | sorted | find | 3 | 0.0434497109999938 |
|
||||
| BST | sorted | find | 4 | 0.04326359800001 |
|
||||
| BST | sorted | find | 5 | 0.0431787990000032 |
|
||||
| BST | sorted | delete | 1 | 0.0546987289999947 |
|
||||
| BST | sorted | delete | 2 | 0.0549414869999793 |
|
||||
| BST | sorted | delete | 3 | 0.0549512879999838 |
|
||||
| BST | sorted | delete | 4 | 0.0546492089999901 |
|
||||
| BST | sorted | delete | 5 | 0.0542962790000274 |
|
||||
Графическое представление результатов приведено на рисунке ниже.
|
||||
[]
|
||||
## 3. Анализ результатов
|
||||
|
||||
|
||||
### 3.1. Вставка
|
||||
|
||||
Связный список: проход по всему списку для поиска дубликата перед вставкой даёт O(n) на каждый элемент. При N = 10 000 это ≈50 млн операций сравнения — отсюда 3.6 с.
|
||||
|
||||
Хеш-таблица: хеш вычисляется за O(len(name)), поиск в корзине ≈ O(1). Итог — 0.037 с независимо от порядка.
|
||||
|
||||
BST на случайных данных: дерево остаётся примерно сбалансированным, высота ≈ log₂(10000) ≈ 13. Итог — 0.034 с. На отсортированных данных каждый новый элемент добавляется в правое поддерево, высота достигает N = 10 000 — полная деградация до O(n²) суммарно. Итог — 12.6 с (×373 замедление).
|
||||
|
||||
|
||||
### 3.2. Поиск
|
||||
|
||||
Связный список: в среднем просматривает N/2 узлов. При 110 запросах — ≈550 000 сравнений. Время ≈ 0.04 с.
|
||||
|
||||
Хеш-таблица: поиск в корзине из ~10 элементов — практически мгновенно. Время ≈ 0.0003 с — в 130 раз быстрее связного списка.
|
||||
|
||||
BST: случайный порядок — log(N) шагов, ≈ 0.0002 с. Отсортированный — линейный поиск O(n), ≈ 0.044 с (сравнимо со связным списком).
|
||||
|
||||
|
||||
### 3.3. Удаление
|
||||
|
||||
Связный список: необходим проход до удаляемого элемента — O(n). При 50 удалениях ≈ 0.02 с.
|
||||
|
||||
Хеш-таблица: O(1) в среднем — ≈ 0.0003 с.
|
||||
|
||||
BST: случайные данные — O(log n) ≈ 0.001 с. Отсортированные — O(n) ≈ 0.055 с.
|
||||
|
||||
|
||||
### 3.4. Получение отсортированного списка
|
||||
|
||||
Связный список и хеш-таблица: сбор всех N элементов + Python sort — O(n log n). Практически одинаково для обеих структур.
|
||||
|
||||
BST: in-order обход уже возвращает отсортированный список — O(n), без дополнительной сортировки. При случайном вводе BST является наиболее эффективным для list_all.
|
||||
|
||||
|
||||
## 4. Выводы и рекомендации
|
||||
|
||||
На основании экспериментов можно дать следующие практические рекомендации:
|
||||
|
||||
Частые вставки и поиск без упорядочивания → Хеш-таблица. Константное среднее время O(1) для всех операций, нечувствительность к порядку данных. Практически всегда лучший выбор для справочников, кэшей, индексов.
|
||||
|
||||
Данные нужны в отсортированном порядке (range queries, итерация по алфавиту) → Сбалансированное BST (AVL, красно-чёрное дерево) или B-дерево. Простая BST допустима только при случайном порядке вставки. На отсортированных данных деградирует до O(n).
|
||||
|
||||
Очень мало элементов или требуется простота реализации → Связный список. При N < 100 разница в скорости незначительна, а код минимальный.
|
||||
|
||||
BST (сбалансированный) vs Хеш-таблица: если нужно только find/insert/delete — хеш-таблица быстрее. Если нужны min/max, range-запросы, сортировка — BST предпочтительнее.
|
||||
|
||||
Итог: для реального телефонного справочника с операциями insert/find/delete оптимальна хеш-таблица. Если требуется регулярный вывод списка по алфавиту — BST (сбалансированное). Связный список применим только как учебная модель или для очень маленьких N.
|
||||
Loading…
Reference in New Issue
Block a user