255 lines
7.8 KiB
Python
255 lines
7.8 KiB
Python
import sys
|
||
sys.setrecursionlimit(20000)
|
||
|
||
csv_path = 'C:/Users/xalva/2026-rff_mp/stepinim/docs/data/results.csv'
|
||
|
||
#Связный список
|
||
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
|
||
|
||
#ТЕСТ
|
||
import random
|
||
random.seed(42)
|
||
N = 10000
|
||
base_records = [(f"User_{i:05d}", f"123-{i:05d}") for i in range(N)]
|
||
records_shuffled = base_records.copy()
|
||
random.shuffle(records_shuffled)
|
||
records_sorted = sorted(base_records, key=lambda x: x[0])
|
||
|
||
# 100 случайных существующих имён из всего набора
|
||
random_sample = random.sample(base_records, 100)
|
||
search_existing = [name for name, _ in random_sample]
|
||
# 10 несуществующих
|
||
search_nonexist = [f"None_{i}" for i in range(10)]
|
||
# 50 случайных для удаления
|
||
delete_sample = random.sample(base_records, 50)
|
||
delete_names = [name for name, _ in delete_sample]
|
||
|
||
import time
|
||
import csv
|
||
import statistics
|
||
|
||
|
||
def measure_operations(records, struct_type):
|
||
results = []
|
||
for rep in range(5):
|
||
if struct_type == 'll':
|
||
head = None
|
||
elif struct_type == 'ht':
|
||
head = [None] * HASH_SIZE
|
||
else:
|
||
root = None
|
||
|
||
start = time.perf_counter()
|
||
if struct_type == 'll':
|
||
for name, phone in records:
|
||
head = ll_insert(head, name, phone)
|
||
elif struct_type == 'ht':
|
||
for name, phone in records:
|
||
ht_insert(head, name, phone)
|
||
else:
|
||
for name, phone in records:
|
||
root = bst_insert(root, name, phone)
|
||
insert_time = time.perf_counter() - start
|
||
results.append((rep + 1, 'insert', insert_time))
|
||
|
||
start = time.perf_counter()
|
||
if struct_type == 'll':
|
||
for name in search_existing + search_nonexist:
|
||
ll_find(head, name)
|
||
elif struct_type == 'ht':
|
||
for name in search_existing + search_nonexist:
|
||
ht_find(head, name)
|
||
else:
|
||
for name in search_existing + search_nonexist:
|
||
bst_find(root, name)
|
||
search_time = time.perf_counter() - start
|
||
results.append((rep + 1, 'search', search_time))
|
||
|
||
start = time.perf_counter()
|
||
if struct_type == 'll':
|
||
for name in delete_names:
|
||
head = ll_delete(head, name)
|
||
elif struct_type == 'ht':
|
||
for name in delete_names:
|
||
ht_delete(head, name)
|
||
else:
|
||
for name in delete_names:
|
||
root = bst_delete(root, name)
|
||
delete_time = time.perf_counter() - start
|
||
results.append((rep + 1, 'delete', delete_time))
|
||
return results
|
||
|
||
all_data = []
|
||
|
||
for struct_name, mode_label, records in [
|
||
("LinkedList", "shuffled", records_shuffled),
|
||
("LinkedList", "sorted", records_sorted),
|
||
("HashTable", "shuffled", records_shuffled),
|
||
("HashTable", "sorted", records_sorted),
|
||
("BST", "shuffled", records_shuffled),
|
||
("BST", "sorted", records_sorted),
|
||
]:
|
||
for rep, op, t in measure_operations(records, struct_name.split('d')[0].lower()[:2] if 'Linked' in struct_name else ('ht' if 'Hash' in struct_name else 'bst')):
|
||
all_data.append([struct_name, mode_label, op, f"{t:.6f}"])
|
||
|
||
|
||
with open(csv_path, 'w', newline='', encoding='utf-8') as f:
|
||
writer = csv.writer(f)
|
||
writer.writerow(["Структура", "Режим", "Операция", "Время (сек)"])
|
||
writer.writerows(all_data)
|
||
|
||
print("CSV сохранён в docs/data/results.csv")
|
||
|
||
|
||
|
||
import matplotlib.pyplot as plt
|
||
import pandas as pd
|
||
|
||
df = pd.read_csv(csv_path)
|
||
|
||
df_avg = df.groupby(['Структура', 'Режим', 'Операция'])['Время (сек)'].mean().reset_index()
|
||
|
||
fig, ax = plt.subplots(figsize=(10,6))
|
||
ops = ['insert', 'search', 'delete']
|
||
x = range(len(ops))
|
||
width = 0.12
|
||
|
||
for i, (struct, mode) in enumerate([('LinkedList','shuffled'),('LinkedList','sorted'),
|
||
('HashTable','shuffled'),('HashTable','sorted'),
|
||
('BST','shuffled'),('BST','sorted')]):
|
||
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])
|
||
ax.set_xticklabels(ops)
|
||
ax.set_ylabel('Среднее время (сек)')
|
||
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
|
||
plt.tight_layout()
|
||
plt.savefig('C:/Users/xalva/2026-rff_mp/stepinim/docs/data/grafik.png')
|
||
plt.show() |