2026-04-28 18:25:45 +00:00
|
|
|
|
import sys
|
2026-05-20 12:19:09 +00:00
|
|
|
|
sys.setrecursionlimit(30000)
|
2026-04-28 18:25:45 +00:00
|
|
|
|
|
2026-05-20 12:19:09 +00:00
|
|
|
|
csv_path = '/stepinim/docs/data/lab1_results.csv'
|
2026-04-28 18:25:45 +00:00
|
|
|
|
|
|
|
|
|
|
#Связный список
|
|
|
|
|
|
def ll_insert(head, name, phone):
|
|
|
|
|
|
new_node = {'name': name, 'phone': phone, 'next': None}
|
|
|
|
|
|
if head is None:
|
|
|
|
|
|
return new_node
|
|
|
|
|
|
|
|
|
|
|
|
curr = head
|
|
|
|
|
|
prev = None
|
|
|
|
|
|
while curr is not None:
|
|
|
|
|
|
if curr['name'] == name:
|
|
|
|
|
|
curr['phone'] = phone
|
|
|
|
|
|
return head
|
|
|
|
|
|
prev = curr
|
|
|
|
|
|
curr = curr['next']
|
|
|
|
|
|
prev['next'] = new_node
|
|
|
|
|
|
return head
|
|
|
|
|
|
|
|
|
|
|
|
def ll_find(head, name):
|
|
|
|
|
|
curr = head
|
|
|
|
|
|
while curr:
|
|
|
|
|
|
if curr['name'] == name:
|
|
|
|
|
|
return curr['phone']
|
|
|
|
|
|
curr = curr['next']
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
def ll_delete(head, name):
|
|
|
|
|
|
if head is None:
|
|
|
|
|
|
return None
|
|
|
|
|
|
if head['name'] == name:
|
|
|
|
|
|
return head['next']
|
|
|
|
|
|
curr = head
|
|
|
|
|
|
while curr['next']:
|
|
|
|
|
|
if curr['next']['name'] == name:
|
|
|
|
|
|
curr['next'] = curr['next']['next']
|
|
|
|
|
|
return head
|
|
|
|
|
|
curr = curr['next']
|
|
|
|
|
|
return head
|
|
|
|
|
|
|
|
|
|
|
|
def ll_list_all(head):
|
|
|
|
|
|
result = []
|
|
|
|
|
|
curr = head
|
|
|
|
|
|
while curr:
|
|
|
|
|
|
result.append((curr['name'], curr['phone']))
|
|
|
|
|
|
curr = curr['next']
|
|
|
|
|
|
result.sort(key=lambda x: x[0])
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
#Хэш-таблица
|
|
|
|
|
|
HASH_SIZE = 1009
|
|
|
|
|
|
def _hash_name(name):
|
|
|
|
|
|
return hash(name) % HASH_SIZE
|
|
|
|
|
|
|
|
|
|
|
|
def ht_insert(buckets, name, phone):
|
|
|
|
|
|
idx = _hash_name(name)
|
|
|
|
|
|
buckets[idx] = ll_insert(buckets[idx], name, phone)
|
|
|
|
|
|
|
|
|
|
|
|
def ht_find(buckets, name):
|
|
|
|
|
|
idx = _hash_name(name)
|
|
|
|
|
|
return ll_find(buckets[idx], name)
|
|
|
|
|
|
|
|
|
|
|
|
def ht_delete(buckets, name):
|
|
|
|
|
|
idx = _hash_name(name)
|
|
|
|
|
|
buckets[idx] = ll_delete(buckets[idx], name)
|
|
|
|
|
|
|
|
|
|
|
|
def ht_list_all(buckets):
|
|
|
|
|
|
all_entries = []
|
|
|
|
|
|
for bucket in buckets:
|
|
|
|
|
|
if bucket is not None:
|
|
|
|
|
|
curr = bucket
|
|
|
|
|
|
while curr:
|
|
|
|
|
|
all_entries.append((curr['name'], curr['phone']))
|
|
|
|
|
|
curr = curr['next']
|
|
|
|
|
|
all_entries.sort(key=lambda x: x[0])
|
|
|
|
|
|
return all_entries
|
|
|
|
|
|
|
|
|
|
|
|
#Двоичное дерево поиска
|
|
|
|
|
|
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):
|
|
|
|
|
|
curr = root
|
|
|
|
|
|
while curr:
|
|
|
|
|
|
if name == curr['name']:
|
|
|
|
|
|
return curr['phone']
|
|
|
|
|
|
elif name < curr['name']:
|
|
|
|
|
|
curr = curr['left']
|
|
|
|
|
|
else:
|
|
|
|
|
|
curr = curr['right']
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
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']:
|
|
|
|
|
|
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):
|
|
|
|
|
|
result = []
|
|
|
|
|
|
def inorder(node):
|
|
|
|
|
|
if node:
|
|
|
|
|
|
inorder(node['left'])
|
|
|
|
|
|
result.append((node['name'], node['phone']))
|
|
|
|
|
|
inorder(node['right'])
|
|
|
|
|
|
inorder(root)
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
2026-05-20 12:19:09 +00:00
|
|
|
|
# ============================================================
|
|
|
|
|
|
# TECT
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
|
|
|
|
|
|
import os
|
2026-04-28 18:25:45 +00:00
|
|
|
|
import random
|
2026-05-20 12:19:09 +00:00
|
|
|
|
import time
|
|
|
|
|
|
import csv
|
|
|
|
|
|
import pandas as pd
|
|
|
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
|
|
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
# ПОДГОТОВКА ПАПОК
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
|
|
|
|
|
|
DATA_DIR = os.path.join("docs", "data")
|
|
|
|
|
|
os.makedirs(DATA_DIR, exist_ok=True)
|
|
|
|
|
|
|
|
|
|
|
|
csv_path = os.path.join(DATA_DIR, "lab1_results.csv")
|
|
|
|
|
|
graph_path = os.path.join(DATA_DIR, "lab1_graph.png")
|
|
|
|
|
|
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
# ТЕСТОВЫЕ ДАННЫЕ
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
|
2026-04-28 18:25:45 +00:00
|
|
|
|
random.seed(42)
|
2026-05-20 12:19:09 +00:00
|
|
|
|
|
|
|
|
|
|
N = 3000
|
|
|
|
|
|
|
|
|
|
|
|
base_records = [
|
|
|
|
|
|
(f"User_{i:05d}", f"123-{i:05d}")
|
|
|
|
|
|
for i in range(N)
|
|
|
|
|
|
]
|
|
|
|
|
|
|
2026-04-28 18:25:45 +00:00
|
|
|
|
records_shuffled = base_records.copy()
|
|
|
|
|
|
random.shuffle(records_shuffled)
|
2026-05-20 12:19:09 +00:00
|
|
|
|
|
2026-04-28 18:25:45 +00:00
|
|
|
|
records_sorted = sorted(base_records, key=lambda x: x[0])
|
|
|
|
|
|
|
2026-05-20 12:19:09 +00:00
|
|
|
|
# Поиск
|
|
|
|
|
|
search_existing = [
|
|
|
|
|
|
name for name, _ in random.sample(base_records, 100)
|
|
|
|
|
|
]
|
2026-04-28 18:25:45 +00:00
|
|
|
|
|
2026-05-20 12:19:09 +00:00
|
|
|
|
search_nonexist = [
|
|
|
|
|
|
f"None_{i}"
|
|
|
|
|
|
for i in range(10)
|
|
|
|
|
|
]
|
2026-04-28 18:25:45 +00:00
|
|
|
|
|
2026-05-20 12:19:09 +00:00
|
|
|
|
# Удаление
|
|
|
|
|
|
delete_names = [
|
|
|
|
|
|
name for name, _ in random.sample(base_records, 50)
|
|
|
|
|
|
]
|
2026-04-28 18:25:45 +00:00
|
|
|
|
|
2026-05-20 12:19:09 +00:00
|
|
|
|
# ============================================================
|
|
|
|
|
|
# СОЗДАНИЕ СТРУКТУР
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
|
|
|
|
|
|
def build_structure(records, struct_type):
|
|
|
|
|
|
|
|
|
|
|
|
if struct_type == "ll":
|
|
|
|
|
|
structure = None
|
|
|
|
|
|
|
|
|
|
|
|
for name, phone in records:
|
|
|
|
|
|
structure = ll_insert(structure, name, phone)
|
|
|
|
|
|
|
|
|
|
|
|
return structure
|
|
|
|
|
|
|
|
|
|
|
|
elif struct_type == "ht":
|
|
|
|
|
|
structure = [None] * HASH_SIZE
|
|
|
|
|
|
|
|
|
|
|
|
for name, phone in records:
|
|
|
|
|
|
ht_insert(structure, name, phone)
|
|
|
|
|
|
|
|
|
|
|
|
return structure
|
|
|
|
|
|
|
|
|
|
|
|
elif struct_type == "bst":
|
|
|
|
|
|
structure = None
|
|
|
|
|
|
|
|
|
|
|
|
for name, phone in records:
|
|
|
|
|
|
structure = bst_insert(structure, name, phone)
|
|
|
|
|
|
|
|
|
|
|
|
return structure
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
# INSERT
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
|
|
|
|
|
|
def measure_insert(records, struct_type):
|
|
|
|
|
|
|
|
|
|
|
|
start = time.perf_counter()
|
|
|
|
|
|
|
|
|
|
|
|
build_structure(records, struct_type)
|
|
|
|
|
|
|
|
|
|
|
|
end = time.perf_counter()
|
|
|
|
|
|
|
|
|
|
|
|
return end - start
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
# SEARCH
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
|
|
|
|
|
|
def measure_search(records, struct_type):
|
|
|
|
|
|
|
|
|
|
|
|
structure = build_structure(records, struct_type)
|
|
|
|
|
|
|
|
|
|
|
|
start = time.perf_counter()
|
|
|
|
|
|
|
|
|
|
|
|
if struct_type == "ll":
|
|
|
|
|
|
for name in search_existing + search_nonexist:
|
|
|
|
|
|
ll_find(structure, name)
|
|
|
|
|
|
|
|
|
|
|
|
elif struct_type == "ht":
|
|
|
|
|
|
for name in search_existing + search_nonexist:
|
|
|
|
|
|
ht_find(structure, name)
|
|
|
|
|
|
|
|
|
|
|
|
elif struct_type == "bst":
|
|
|
|
|
|
for name in search_existing + search_nonexist:
|
|
|
|
|
|
bst_find(structure, name)
|
|
|
|
|
|
|
|
|
|
|
|
end = time.perf_counter()
|
|
|
|
|
|
|
|
|
|
|
|
return end - start
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
# DELETE
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
|
|
|
|
|
|
def measure_delete(records, struct_type):
|
|
|
|
|
|
|
|
|
|
|
|
structure = build_structure(records, struct_type)
|
|
|
|
|
|
|
|
|
|
|
|
start = time.perf_counter()
|
|
|
|
|
|
|
|
|
|
|
|
if struct_type == "ll":
|
|
|
|
|
|
for name in delete_names:
|
|
|
|
|
|
structure = ll_delete(structure, name)
|
|
|
|
|
|
|
|
|
|
|
|
elif struct_type == "ht":
|
|
|
|
|
|
for name in delete_names:
|
|
|
|
|
|
ht_delete(structure, name)
|
|
|
|
|
|
|
|
|
|
|
|
elif struct_type == "bst":
|
|
|
|
|
|
for name in delete_names:
|
|
|
|
|
|
structure = bst_delete(structure, name)
|
|
|
|
|
|
|
|
|
|
|
|
end = time.perf_counter()
|
|
|
|
|
|
|
|
|
|
|
|
return end - start
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
# ЗАМЕРЫ
|
|
|
|
|
|
# ============================================================
|
2026-04-28 18:25:45 +00:00
|
|
|
|
|
|
|
|
|
|
all_data = []
|
|
|
|
|
|
|
2026-05-20 12:19:09 +00:00
|
|
|
|
experiments = [
|
|
|
|
|
|
("LinkedList", "ll"),
|
|
|
|
|
|
("HashTable", "ht"),
|
|
|
|
|
|
("BST", "bst")
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
modes = [
|
|
|
|
|
|
("shuffled", records_shuffled),
|
|
|
|
|
|
("sorted", records_sorted)
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
for struct_name, struct_type in experiments:
|
|
|
|
|
|
|
|
|
|
|
|
for mode_name, records in modes:
|
|
|
|
|
|
|
|
|
|
|
|
for rep in range(1, 4):
|
2026-04-28 18:25:45 +00:00
|
|
|
|
|
2026-05-20 12:19:09 +00:00
|
|
|
|
insert_time = measure_insert(records, struct_type)
|
|
|
|
|
|
|
|
|
|
|
|
search_time = measure_search(records, struct_type)
|
|
|
|
|
|
|
|
|
|
|
|
delete_time = measure_delete(records, struct_type)
|
|
|
|
|
|
|
|
|
|
|
|
all_data.append([
|
|
|
|
|
|
struct_name,
|
|
|
|
|
|
mode_name,
|
|
|
|
|
|
rep,
|
|
|
|
|
|
"insert",
|
|
|
|
|
|
insert_time
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
all_data.append([
|
|
|
|
|
|
struct_name,
|
|
|
|
|
|
mode_name,
|
|
|
|
|
|
rep,
|
|
|
|
|
|
"search",
|
|
|
|
|
|
search_time
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
all_data.append([
|
|
|
|
|
|
struct_name,
|
|
|
|
|
|
mode_name,
|
|
|
|
|
|
rep,
|
|
|
|
|
|
"delete",
|
|
|
|
|
|
delete_time
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
# CSV
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
|
|
|
|
|
|
with open(csv_path, "w", newline="", encoding="utf-8") as f:
|
2026-04-28 18:25:45 +00:00
|
|
|
|
|
|
|
|
|
|
writer = csv.writer(f)
|
|
|
|
|
|
|
2026-05-20 12:19:09 +00:00
|
|
|
|
writer.writerow([
|
|
|
|
|
|
"Структура",
|
|
|
|
|
|
"Режим",
|
|
|
|
|
|
"Повтор",
|
|
|
|
|
|
"Операция",
|
|
|
|
|
|
"Время (сек)"
|
|
|
|
|
|
])
|
2026-04-28 18:25:45 +00:00
|
|
|
|
|
2026-05-20 12:19:09 +00:00
|
|
|
|
writer.writerows(all_data)
|
2026-04-28 18:25:45 +00:00
|
|
|
|
|
2026-05-20 12:19:09 +00:00
|
|
|
|
print(f"CSV сохранён: {csv_path}")
|
2026-04-28 18:25:45 +00:00
|
|
|
|
|
2026-05-20 12:19:09 +00:00
|
|
|
|
# ============================================================
|
|
|
|
|
|
# ГРАФИК
|
|
|
|
|
|
# ============================================================
|
2026-04-28 18:25:45 +00:00
|
|
|
|
|
|
|
|
|
|
df = pd.read_csv(csv_path)
|
|
|
|
|
|
|
2026-05-20 12:19:09 +00:00
|
|
|
|
df_avg = (
|
|
|
|
|
|
df.groupby(
|
|
|
|
|
|
["Структура", "Режим", "Операция"]
|
|
|
|
|
|
)["Время (сек)"]
|
|
|
|
|
|
.mean()
|
|
|
|
|
|
.reset_index()
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
fig, ax = plt.subplots(figsize=(12, 6))
|
|
|
|
|
|
|
|
|
|
|
|
ops = ["insert", "search", "delete"]
|
2026-04-28 18:25:45 +00:00
|
|
|
|
|
|
|
|
|
|
x = range(len(ops))
|
2026-05-20 12:19:09 +00:00
|
|
|
|
|
2026-04-28 18:25:45 +00:00
|
|
|
|
width = 0.12
|
|
|
|
|
|
|
2026-05-20 12:19:09 +00:00
|
|
|
|
configs = [
|
|
|
|
|
|
("LinkedList", "shuffled"),
|
|
|
|
|
|
("LinkedList", "sorted"),
|
|
|
|
|
|
("HashTable", "shuffled"),
|
|
|
|
|
|
("HashTable", "sorted"),
|
|
|
|
|
|
("BST", "shuffled"),
|
|
|
|
|
|
("BST", "sorted")
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
for i, (struct, mode) in enumerate(configs):
|
|
|
|
|
|
|
|
|
|
|
|
subset = df_avg[
|
|
|
|
|
|
(df_avg["Структура"] == struct)
|
|
|
|
|
|
&
|
|
|
|
|
|
(df_avg["Режим"] == mode)
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
times = [
|
|
|
|
|
|
subset[
|
|
|
|
|
|
subset["Операция"] == op
|
|
|
|
|
|
]["Время (сек)"].values[0]
|
|
|
|
|
|
for op in ops
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
ax.bar(
|
|
|
|
|
|
[p + i * width for p in x],
|
|
|
|
|
|
times,
|
|
|
|
|
|
width,
|
|
|
|
|
|
label=f"{struct} ({mode})"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
ax.set_xticks([p + 2.5 * width for p in x])
|
2026-04-28 18:25:45 +00:00
|
|
|
|
|
|
|
|
|
|
ax.set_xticklabels(ops)
|
2026-05-20 12:19:09 +00:00
|
|
|
|
|
|
|
|
|
|
ax.set_ylabel("Среднее время (сек)")
|
|
|
|
|
|
|
|
|
|
|
|
ax.set_title("Сравнение структур данных")
|
|
|
|
|
|
|
|
|
|
|
|
ax.legend(
|
|
|
|
|
|
bbox_to_anchor=(1.05, 1),
|
|
|
|
|
|
loc="upper left"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-04-28 18:25:45 +00:00
|
|
|
|
plt.tight_layout()
|
2026-05-20 12:19:09 +00:00
|
|
|
|
|
|
|
|
|
|
plt.savefig(graph_path)
|
|
|
|
|
|
|
|
|
|
|
|
print(f"График сохранён: {graph_path}")
|
|
|
|
|
|
|
2026-04-28 18:25:45 +00:00
|
|
|
|
plt.show()
|