[1] zadanie_1
This commit is contained in:
parent
a2621ad508
commit
8468109b7c
282
zverevem/lab1/docs/data/experiments.py
Normal file
282
zverevem/lab1/docs/data/experiments.py
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
import random
|
||||
import time
|
||||
import csv
|
||||
import os
|
||||
from phonebook import *
|
||||
|
||||
def generate_test_data(n=10000):
|
||||
|
||||
records = [(f"User_{i:05d}", f"+7-999-{i:07d}") for i in range(n)]
|
||||
|
||||
records_shuffled = records.copy()
|
||||
random.shuffle(records_shuffled)
|
||||
|
||||
records_sorted = sorted(records, key=lambda x: x[0])
|
||||
|
||||
return records_shuffled, records_sorted
|
||||
|
||||
def get_random_names(records, n=100):
|
||||
return[name for name, _ in random.sample(records, min(n, len(records)))]
|
||||
|
||||
def run_linked_experiments(records, mode_name):
|
||||
|
||||
print(f"\n связный список ({mode_name}):")
|
||||
|
||||
print("вставка 10000 записей:")
|
||||
|
||||
insert_times = []
|
||||
for run in range(5):
|
||||
start = time.perf_counter()
|
||||
head = None
|
||||
for name, phone in records:
|
||||
head = ll_insert(head, name, phone)
|
||||
end = time.perf_counter()
|
||||
insert_times.append(end - start)
|
||||
print(f"Вставка {run+1}/5: {insert_times[-1]:.6f} сек")
|
||||
|
||||
avg_insert = sum(insert_times) / 5
|
||||
print(f"среднее: {avg_insert:.6f} сек")
|
||||
|
||||
print("поиск 110 записей:")
|
||||
|
||||
exist_names = get_random_names(records, 100)
|
||||
non_exist_names = [f"None_{i}" for i in range(10)]
|
||||
|
||||
find_times = []
|
||||
for run in range(5):
|
||||
start = time.perf_counter()
|
||||
|
||||
for name in exist_names:
|
||||
ll_find(head, name)
|
||||
for name in non_exist_names:
|
||||
ll_find(head, name)
|
||||
|
||||
end = time.perf_counter()
|
||||
find_times.append(end - start)
|
||||
print(f"поиск {run+1}/5: {find_times[-1]:.6f} сек")
|
||||
|
||||
avg_find = sum(find_times) / 5
|
||||
print(f"среднее: {avg_find:.6f} сек")
|
||||
|
||||
print("удаление 50 случайных записей:")
|
||||
|
||||
to_delete = get_random_names(records,50)
|
||||
|
||||
delete_times = []
|
||||
for run in range(5):
|
||||
current_head = head
|
||||
start = time.perf_counter()
|
||||
for name in to_delete:
|
||||
current_head = ll_delete(current_head, name)
|
||||
end = time.perf_counter()
|
||||
delete_times.append(end - start)
|
||||
print(f"удаление {run+1}/5: {delete_times[-1]:.6f} сек")
|
||||
|
||||
avg_delete = sum(delete_times) / 5
|
||||
print(f"среднее: {avg_delete:.6f} сек")
|
||||
|
||||
return{
|
||||
'structure': 'LinkedList',
|
||||
'mode': mode_name,
|
||||
'insert_avg': avg_insert,
|
||||
'insert_all': insert_times,
|
||||
'find_avg': avg_find,
|
||||
'find_all': find_times,
|
||||
'delete_avg': avg_delete,
|
||||
'delete_all': delete_times
|
||||
}
|
||||
|
||||
def run_hash_experiments(records, mode_name):
|
||||
|
||||
print(f"\n хеш-таблица({mode_name})")
|
||||
|
||||
print("вставка 10000 записей:")
|
||||
|
||||
insert_times = []
|
||||
for run in range(5):
|
||||
start = time.perf_counter()
|
||||
|
||||
buckets = ht_create(1000)
|
||||
for name, phone in records:
|
||||
buckets = ht_insert(buckets, name, phone)
|
||||
|
||||
end = time.perf_counter()
|
||||
insert_times.append(end - start)
|
||||
print(f"Вставка {run+1}/5: {insert_times[-1]:.6f} сек")
|
||||
|
||||
avg_insert = sum(insert_times) / 5
|
||||
print(f"среднее: {avg_insert:.6f} сек")
|
||||
|
||||
print("поиск 110 записей:")
|
||||
|
||||
exist_names = get_random_names(records, 100)
|
||||
non_exist_names = [f"None_{i}" for i in range(10)]
|
||||
|
||||
find_times = []
|
||||
for run in range(5):
|
||||
start = time.perf_counter()
|
||||
|
||||
for name in exist_names:
|
||||
ht_find(buckets, name)
|
||||
for name in non_exist_names:
|
||||
ht_find(buckets, name)
|
||||
|
||||
end = time.perf_counter()
|
||||
find_times.append(end - start)
|
||||
print(f"поиск {run+1}/5: {find_times[-1]:.6f} сек")
|
||||
|
||||
avg_find = sum(find_times) / 5
|
||||
print(f"среднее: {avg_find:.6f} сек")
|
||||
|
||||
print("удаление 50 случайных записей:")
|
||||
|
||||
to_delete = get_random_names(records,50)
|
||||
|
||||
delete_times = []
|
||||
for run in range(5):
|
||||
current_buckets = buckets.copy()
|
||||
start = time.perf_counter()
|
||||
for name in to_delete:
|
||||
current_buckets = ht_delete(current_buckets, name)
|
||||
end = time.perf_counter()
|
||||
delete_times.append(end - start)
|
||||
print(f"удаление {run+1}/5: {delete_times[-1]:.6f} сек")
|
||||
|
||||
avg_delete = sum(delete_times) / 5
|
||||
print(f"среднее: {avg_delete:.6f} сек")
|
||||
|
||||
return{
|
||||
'structure': 'HashTable',
|
||||
'mode': mode_name,
|
||||
'insert_avg': avg_insert,
|
||||
'insert_all': insert_times,
|
||||
'find_avg': avg_find,
|
||||
'find_all': find_times,
|
||||
'delete_avg': avg_delete,
|
||||
'delete_all': delete_times
|
||||
}
|
||||
|
||||
def run_bst_experiments(records, mode_name):
|
||||
|
||||
print(f"\n двоичное дерево({mode_name})")
|
||||
|
||||
print("вставка 10000 записей:")
|
||||
|
||||
insert_times = []
|
||||
for run in range(5):
|
||||
start = time.perf_counter()
|
||||
|
||||
root = None
|
||||
for name, phone in records:
|
||||
root = bst_insert(root, name, phone)
|
||||
|
||||
end = time.perf_counter()
|
||||
insert_times.append(end - start)
|
||||
print(f"Вставка {run+1}/5: {insert_times[-1]:.6f} сек")
|
||||
|
||||
avg_insert = sum(insert_times) / 5
|
||||
print(f"среднее: {avg_insert:.6f} сек")
|
||||
|
||||
print("поиск 110 записей:")
|
||||
|
||||
exist_names = get_random_names(records, 100)
|
||||
non_exist_names = [f"None_{i}" for i in range(10)]
|
||||
|
||||
find_times = []
|
||||
for run in range(5):
|
||||
start = time.perf_counter()
|
||||
|
||||
for name in exist_names:
|
||||
bst_find(root, name)
|
||||
for name in non_exist_names:
|
||||
bst_find(root, name)
|
||||
|
||||
end = time.perf_counter()
|
||||
find_times.append(end - start)
|
||||
print(f"поиск {run+1}/5: {find_times[-1]:.6f} сек")
|
||||
|
||||
avg_find = sum(find_times) / 5
|
||||
print(f"среднее: {avg_find:.6f} сек")
|
||||
|
||||
print("удаление 50 случайных записей:")
|
||||
|
||||
to_delete = get_random_names(records,50)
|
||||
|
||||
delete_times = []
|
||||
for run in range(5):
|
||||
current_root = root
|
||||
start = time.perf_counter()
|
||||
for name in to_delete:
|
||||
current_root = bst_delete(current_root, name)
|
||||
end = time.perf_counter()
|
||||
delete_times.append(end - start)
|
||||
print(f"удаление {run+1}/5: {delete_times[-1]:.6f} сек")
|
||||
|
||||
avg_delete = sum(delete_times) / 5
|
||||
print(f"среднее: {avg_delete:.6f} сек")
|
||||
|
||||
return{
|
||||
'structure': 'BST',
|
||||
'mode': mode_name,
|
||||
'insert_avg': avg_insert,
|
||||
'insert_all': insert_times,
|
||||
'find_avg': avg_find,
|
||||
'find_all': find_times,
|
||||
'delete_avg': avg_delete,
|
||||
'delete_all': delete_times
|
||||
}
|
||||
|
||||
def save_results_to_csv(all_results):
|
||||
|
||||
os.makedirs("docs/data", exist_ok=True)
|
||||
|
||||
with open("docs/data/results.csv", "w", encoding="utf-8") as f:
|
||||
|
||||
f.write("Структура, Режим, Операция, Замер, Время (сек)\n")
|
||||
|
||||
for res in all_results:
|
||||
struct = res['structure']
|
||||
mode = res['mode']
|
||||
|
||||
|
||||
for i, t in enumerate(res['insert_all']):
|
||||
f.write(f"{struct},{mode},вставка,{i+1},{t}\n")
|
||||
f.write(f"{struct},{mode},вставка,среднее,{res['insert_avg']}\n")
|
||||
|
||||
|
||||
for i, t in enumerate(res['find_all']):
|
||||
f.write(f"{struct},{mode},поиск,{i+1},{t}\n")
|
||||
f.write(f"{struct},{mode},поиск,среднее,{res['find_avg']}\n")
|
||||
|
||||
|
||||
for i, t in enumerate(res['delete_all']):
|
||||
f.write(f"{struct},{mode},удаление,{i+1},{t}\n")
|
||||
f.write(f"{struct},{mode},удаление,среднее,{res['delete_avg']}\n")
|
||||
|
||||
|
||||
def main():
|
||||
print("эксперименты по замеру производительности")
|
||||
|
||||
records_shuffled, records_sorted = generate_test_data(10000)
|
||||
|
||||
all_results = []
|
||||
|
||||
print("режим: случайный порядок")
|
||||
|
||||
all_results.append(run_linked_experiments(records_shuffled, "случайный"))
|
||||
all_results.append(run_hash_experiments(records_shuffled, "случайный"))
|
||||
all_results.append(run_bst_experiments(records_shuffled, "случайный"))
|
||||
|
||||
print("режим: отсортированный порядок")
|
||||
|
||||
all_results.append(run_linked_experiments(records_sorted, "отсортированный"))
|
||||
all_results.append(run_hash_experiments(records_sorted, "отсортированный"))
|
||||
all_results.append(run_bst_experiments(records_sorted, "отсортированный"))
|
||||
|
||||
save_results_to_csv(all_results)
|
||||
|
||||
if __name__== "__main__":
|
||||
main()
|
||||
|
||||
|
||||
|
||||
BIN
zverevem/lab1/docs/data/graph_delete.png
Normal file
BIN
zverevem/lab1/docs/data/graph_delete.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
BIN
zverevem/lab1/docs/data/graph_insert.png
Normal file
BIN
zverevem/lab1/docs/data/graph_insert.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
BIN
zverevem/lab1/docs/data/graph_search.png
Normal file
BIN
zverevem/lab1/docs/data/graph_search.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
123
zverevem/lab1/docs/data/make_graphs.py
Normal file
123
zverevem/lab1/docs/data/make_graphs.py
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import os
|
||||
|
||||
|
||||
os.makedirs('docs/data', exist_ok=True)
|
||||
|
||||
|
||||
structures = ['LinkedList', 'HashTable', 'BST']
|
||||
|
||||
random_insert = [0.0037545, 0.015088, 0.026280]
|
||||
sorted_insert = [0.0017544, 0.011369, 4.930788]
|
||||
|
||||
random_search = [0.00000962, 0.0001646, 0.0002592]
|
||||
sorted_search = [0.00000858, 0.00014016, 0.047126]
|
||||
|
||||
random_delete = [0.0000079, 0.00009824, 0.00016984]
|
||||
sorted_delete = [0.00000294, 0.00005878, 0.023013]
|
||||
|
||||
x = np.arange(len(structures))
|
||||
width = 0.35
|
||||
|
||||
#график вставка
|
||||
fig, ax = plt.subplots(figsize=(12, 7))
|
||||
|
||||
bars1 = ax.bar(x - width/2, random_insert, width, label='Случайный порядок', color='#3498db')
|
||||
bars2 = ax.bar(x + width/2, sorted_insert, width, label='Отсортированный порядок', color='#e74c3c')
|
||||
|
||||
|
||||
for bar in bars1:
|
||||
height = bar.get_height()
|
||||
ax.annotate(f'{height:.4f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
|
||||
for bar in bars2:
|
||||
height = bar.get_height()
|
||||
if height < 1:
|
||||
ax.annotate(f'{height:.4f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
else:
|
||||
ax.annotate(f'{height:.1f} сек', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 5), textcoords="offset points", ha='center', va='bottom', fontsize=10, fontweight='bold')
|
||||
|
||||
ax.set_ylabel('Время (сек)', fontsize=12)
|
||||
ax.set_title('Время вставки 10000 записей', fontsize=14, fontweight='bold')
|
||||
ax.set_xticks(x)
|
||||
ax.set_xticklabels(structures, fontsize=11)
|
||||
ax.legend(fontsize=11)
|
||||
ax.set_yscale('log')
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig('docs/data/graph_insert.png', dpi=150, bbox_inches='tight')
|
||||
plt.close()
|
||||
|
||||
|
||||
# график поиск
|
||||
fig, ax = plt.subplots(figsize=(12, 7))
|
||||
|
||||
bars1 = ax.bar(x - width/2, random_search, width, label='Случайный порядок', color='#3498db')
|
||||
bars2 = ax.bar(x + width/2, sorted_search, width, label='Отсортированный порядок', color='#e74c3c')
|
||||
|
||||
for bar in bars1:
|
||||
height = bar.get_height()
|
||||
ax.annotate(f'{height:.6f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
|
||||
for bar in bars2:
|
||||
height = bar.get_height()
|
||||
if height < 0.01:
|
||||
ax.annotate(f'{height:.6f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
else:
|
||||
ax.annotate(f'{height:.4f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
|
||||
ax.set_ylabel('Время (сек)', fontsize=12)
|
||||
ax.set_title('Время поиска 110 записей', fontsize=14, fontweight='bold')
|
||||
ax.set_xticks(x)
|
||||
ax.set_xticklabels(structures, fontsize=11)
|
||||
ax.legend(fontsize=11)
|
||||
ax.set_yscale('log')
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig('docs/data/graph_search.png', dpi=150, bbox_inches='tight')
|
||||
plt.close()
|
||||
|
||||
|
||||
# график удаление
|
||||
fig, ax = plt.subplots(figsize=(12, 7))
|
||||
|
||||
bars1 = ax.bar(x - width/2, random_delete, width, label='Случайный порядок', color='#3498db')
|
||||
bars2 = ax.bar(x + width/2, sorted_delete, width, label='Отсортированный порядок', color='#e74c3c')
|
||||
|
||||
for bar in bars1:
|
||||
height = bar.get_height()
|
||||
ax.annotate(f'{height:.6f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
|
||||
for bar in bars2:
|
||||
height = bar.get_height()
|
||||
if height < 0.01:
|
||||
ax.annotate(f'{height:.6f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
else:
|
||||
ax.annotate(f'{height:.4f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
|
||||
ax.set_ylabel('Время (сек)', fontsize=12)
|
||||
ax.set_title('Время удаления 50 записей', fontsize=14, fontweight='bold')
|
||||
ax.set_xticks(x)
|
||||
ax.set_xticklabels(structures, fontsize=11)
|
||||
ax.legend(fontsize=11)
|
||||
ax.set_yscale('log')
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig('docs/data/graph_delete.png', dpi=150, bbox_inches='tight')
|
||||
plt.close()
|
||||
|
||||
|
||||
34
zverevem/lab1/docs/data/make_tables.py
Normal file
34
zverevem/lab1/docs/data/make_tables.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
import matplotlib.pyplot as plt
|
||||
import os
|
||||
|
||||
os.makedirs('docs/data', exist_ok=True)
|
||||
|
||||
data = [
|
||||
['LinkedList', 'случайный', 0.0037545, 0.00000962, 0.0000079],
|
||||
['HashTable', 'случайный', 0.015088, 0.0001646, 0.00009824],
|
||||
['BST', 'случайный', 0.026280, 0.0002592, 0.00016984],
|
||||
['LinkedList', 'отсортированный', 0.0017544, 0.00000858, 0.00000294],
|
||||
['HashTable', 'отсортированный', 0.011369, 0.00014016, 0.00005878],
|
||||
['BST', 'отсортированный', 4.930788, 0.047126, 0.023013],
|
||||
]
|
||||
|
||||
fig, ax = plt.subplots(figsize=(12, 5))
|
||||
ax.axis('tight')
|
||||
ax.axis('off')
|
||||
|
||||
columns = ['Структура', 'Режим', 'Вставка (10000)', 'Поиск (110)', 'Удаление (50)']
|
||||
table = ax.table(cellText=data, colLabels=columns, loc='center', cellLoc='center')
|
||||
|
||||
table.auto_set_font_size(False)
|
||||
table.set_fontsize(10)
|
||||
table.scale(1.2, 1.5)
|
||||
|
||||
for i, row in enumerate(data):
|
||||
if row[0] == 'BST' and row[2] > 1:
|
||||
table[(i+1, 2)].set_facecolor('#ffcccc')
|
||||
table[(i+1, 2)].set_text_props(weight='bold')
|
||||
|
||||
plt.title('Результаты экспериментов (среднее время в секундах)', fontsize=14, fontweight='bold', pad=20)
|
||||
plt.savefig('docs/data/table_results.png', dpi=200, bbox_inches='tight', facecolor='white')
|
||||
plt.close()
|
||||
195
zverevem/lab1/docs/data/phonebook.py
Normal file
195
zverevem/lab1/docs/data/phonebook.py
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
def ll_insert(head, name, phone):
|
||||
|
||||
new_node = {'name': name, 'phone': phone, 'next': None}
|
||||
|
||||
if head is None:
|
||||
return new_node
|
||||
|
||||
current = head
|
||||
while current['next'] is not None:
|
||||
current = current['next']
|
||||
|
||||
current['next'] = new_node
|
||||
return head
|
||||
|
||||
def ll_find(head, name):
|
||||
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):
|
||||
records = []
|
||||
current = head
|
||||
|
||||
while current is not None:
|
||||
records.append((current['name'], current['phone']))
|
||||
current = current['next']
|
||||
records.sort(key=lambda x: x[0])
|
||||
return records
|
||||
|
||||
def hash_function(name, table_size):
|
||||
total = 0
|
||||
for ch in name:
|
||||
total = (total*31 + ord(ch)) % table_size
|
||||
return total
|
||||
|
||||
def ht_create(size=1000):
|
||||
return [None]*size
|
||||
|
||||
def ht_insert(buckets, name, phone):
|
||||
idx = hash_function(name, len(buckets))
|
||||
buckets[idx] = ll_insert(buckets[idx], name, phone)
|
||||
return buckets
|
||||
|
||||
def ht_find(buckets, name):
|
||||
idx = hash_function(name, len(buckets))
|
||||
return ll_find(buckets[idx], name)
|
||||
|
||||
def ht_delete(buckets, name):
|
||||
idx = hash_function(name, len(buckets))
|
||||
buckets[idx] = ll_delete(buckets[idx], name)
|
||||
return buckets
|
||||
|
||||
def ht_list_all(buckets):
|
||||
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
|
||||
|
||||
def bst_insert(root, name, phone):
|
||||
|
||||
new_node = {'name': name, 'phone': phone, 'left': None, 'right': None}
|
||||
|
||||
if root is None:
|
||||
return new_node
|
||||
|
||||
current = root
|
||||
while True:
|
||||
if name < current['name']:
|
||||
if current['left'] is None:
|
||||
current['left'] = new_node
|
||||
break
|
||||
current = current['left']
|
||||
elif name > current['name']:
|
||||
if current['right'] is None:
|
||||
current['right'] = new_node
|
||||
break
|
||||
current = current['right']
|
||||
else:
|
||||
current['phone'] = phone
|
||||
break
|
||||
|
||||
return root
|
||||
|
||||
|
||||
def bst_find(root, name):
|
||||
|
||||
current = root
|
||||
while current is not None:
|
||||
if name == current['name']:
|
||||
return current['phone']
|
||||
elif name < current['name']:
|
||||
current = current['left']
|
||||
else:
|
||||
current = current['right']
|
||||
return None
|
||||
|
||||
|
||||
def _bst_find_min(node):
|
||||
|
||||
current = node
|
||||
while current['left'] is not None:
|
||||
current = current['left']
|
||||
return current
|
||||
|
||||
|
||||
def bst_delete(root, name):
|
||||
|
||||
if root is None:
|
||||
return None
|
||||
|
||||
parent = None
|
||||
current = root
|
||||
|
||||
while current is not None and current['name'] != name:
|
||||
parent = current
|
||||
if name < current['name']:
|
||||
current = current['left']
|
||||
else:
|
||||
current = current['right']
|
||||
|
||||
if current is None:
|
||||
return root
|
||||
|
||||
if current['left'] is None and current['right'] is None:
|
||||
if parent is None:
|
||||
return None
|
||||
if parent['left'] == current:
|
||||
parent['left'] = None
|
||||
else:
|
||||
parent['right'] = None
|
||||
return root
|
||||
|
||||
if current['left'] is None:
|
||||
child = current['right']
|
||||
elif current['right'] is None:
|
||||
child = current['left']
|
||||
else:
|
||||
successor_parent = current
|
||||
successor = current['right']
|
||||
while successor['left'] is not None:
|
||||
successor_parent = successor
|
||||
successor = successor['left']
|
||||
|
||||
current['name'] = successor['name']
|
||||
current['phone'] = successor['phone']
|
||||
|
||||
if successor_parent['left'] == successor:
|
||||
successor_parent['left'] = successor['right']
|
||||
else:
|
||||
successor_parent['right'] = successor['right']
|
||||
|
||||
return root
|
||||
|
||||
if parent is None:
|
||||
return child
|
||||
if parent['left'] == current:
|
||||
parent['left'] = child
|
||||
else:
|
||||
parent['right'] = child
|
||||
|
||||
return root
|
||||
|
||||
|
||||
def bst_list_all(root):
|
||||
records = []
|
||||
|
||||
def inorder(node):
|
||||
if node is None:
|
||||
return
|
||||
inorder(node['left'])
|
||||
records.append((node['name'], node['phone']))
|
||||
inorder(node['right'])
|
||||
|
||||
inorder(root)
|
||||
return records
|
||||
109
zverevem/lab1/docs/data/results.csv
Normal file
109
zverevem/lab1/docs/data/results.csv
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
Структура, Режим, Операция, Замер, Время (сек)
|
||||
LinkedList,случайный,вставка,1,0.0013560999650508165
|
||||
LinkedList,случайный,вставка,2,0.0015854999655857682
|
||||
LinkedList,случайный,вставка,3,0.0012766000581905246
|
||||
LinkedList,случайный,вставка,4,0.0013539999490603805
|
||||
LinkedList,случайный,вставка,5,0.0013421999756246805
|
||||
LinkedList,случайный,вставка,среднее,0.001382879982702434
|
||||
LinkedList,случайный,поиск,1,5.899928510189056e-06
|
||||
LinkedList,случайный,поиск,2,4.899920895695686e-06
|
||||
LinkedList,случайный,поиск,3,4.800036549568176e-06
|
||||
LinkedList,случайный,поиск,4,5.09992241859436e-06
|
||||
LinkedList,случайный,поиск,5,5.00003807246685e-06
|
||||
LinkedList,случайный,поиск,среднее,5.139969289302826e-06
|
||||
LinkedList,случайный,удаление,1,2.900022082030773e-06
|
||||
LinkedList,случайный,удаление,2,2.199900336563587e-06
|
||||
LinkedList,случайный,удаление,3,2.200016751885414e-06
|
||||
LinkedList,случайный,удаление,4,2.100015990436077e-06
|
||||
LinkedList,случайный,удаление,5,2.299901098012924e-06
|
||||
LinkedList,случайный,удаление,среднее,2.3399712517857552e-06
|
||||
HashTable,случайный,вставка,1,0.010439200093969703
|
||||
HashTable,случайный,вставка,2,0.010059899999760091
|
||||
HashTable,случайный,вставка,3,0.010718900011852384
|
||||
HashTable,случайный,вставка,4,0.010526199941523373
|
||||
HashTable,случайный,вставка,5,0.0102259999839589
|
||||
HashTable,случайный,вставка,среднее,0.01039404000621289
|
||||
HashTable,случайный,поиск,1,9.549991227686405e-05
|
||||
HashTable,случайный,поиск,2,9.079999290406704e-05
|
||||
HashTable,случайный,поиск,3,9.989994578063488e-05
|
||||
HashTable,случайный,поиск,4,9.240000508725643e-05
|
||||
HashTable,случайный,поиск,5,9.039998985826969e-05
|
||||
HashTable,случайный,поиск,среднее,9.379996918141842e-05
|
||||
HashTable,случайный,удаление,1,4.610000178217888e-05
|
||||
HashTable,случайный,удаление,2,4.2499974370002747e-05
|
||||
HashTable,случайный,удаление,3,4.290009383112192e-05
|
||||
HashTable,случайный,удаление,4,4.2400090023875237e-05
|
||||
HashTable,случайный,удаление,5,4.269997589290142e-05
|
||||
HashTable,случайный,удаление,среднее,4.332002718001604e-05
|
||||
BST,случайный,вставка,1,0.014894199906848371
|
||||
BST,случайный,вставка,2,0.015171999926678836
|
||||
BST,случайный,вставка,3,0.015123400022275746
|
||||
BST,случайный,вставка,4,0.015276000020094216
|
||||
BST,случайный,вставка,5,0.01524280000012368
|
||||
BST,случайный,вставка,среднее,0.015141679975204169
|
||||
BST,случайный,поиск,1,0.00014160003047436476
|
||||
BST,случайный,поиск,2,0.0001335999695584178
|
||||
BST,случайный,поиск,3,0.00013259996194392443
|
||||
BST,случайный,поиск,4,0.0001449999399483204
|
||||
BST,случайный,поиск,5,0.00013129995204508305
|
||||
BST,случайный,поиск,среднее,0.0001368199707940221
|
||||
BST,случайный,удаление,1,8.909997995942831e-05
|
||||
BST,случайный,удаление,2,6.929994560778141e-05
|
||||
BST,случайный,удаление,3,6.719992961734533e-05
|
||||
BST,случайный,удаление,4,6.700004450976849e-05
|
||||
BST,случайный,удаление,5,6.679992657154799e-05
|
||||
BST,случайный,удаление,среднее,7.18799652531743e-05
|
||||
LinkedList,отсортированный,вставка,1,0.0012123000342398882
|
||||
LinkedList,отсортированный,вставка,2,0.0011566999601200223
|
||||
LinkedList,отсортированный,вставка,3,0.001145699992775917
|
||||
LinkedList,отсортированный,вставка,4,0.0011751001002267003
|
||||
LinkedList,отсортированный,вставка,5,0.0011464999988675117
|
||||
LinkedList,отсортированный,вставка,среднее,0.0011672600172460078
|
||||
LinkedList,отсортированный,поиск,1,5.300040356814861e-06
|
||||
LinkedList,отсортированный,поиск,2,4.900037311017513e-06
|
||||
LinkedList,отсортированный,поиск,3,4.800036549568176e-06
|
||||
LinkedList,отсортированный,поиск,4,5.200039595365524e-06
|
||||
LinkedList,отсортированный,поиск,5,4.799920134246349e-06
|
||||
LinkedList,отсортированный,поиск,среднее,5.000014789402485e-06
|
||||
LinkedList,отсортированный,удаление,1,2.400018274784088e-06
|
||||
LinkedList,отсортированный,удаление,2,2.300017513334751e-06
|
||||
LinkedList,отсортированный,удаление,3,2.300017513334751e-06
|
||||
LinkedList,отсортированный,удаление,4,2.300017513334751e-06
|
||||
LinkedList,отсортированный,удаление,5,2.200016751885414e-06
|
||||
LinkedList,отсортированный,удаление,среднее,2.300017513334751e-06
|
||||
HashTable,отсортированный,вставка,1,0.00947619997896254
|
||||
HashTable,отсортированный,вставка,2,0.00943189999088645
|
||||
HashTable,отсортированный,вставка,3,0.009878099896013737
|
||||
HashTable,отсортированный,вставка,4,0.009515199926681817
|
||||
HashTable,отсортированный,вставка,5,0.009485000045970082
|
||||
HashTable,отсортированный,вставка,среднее,0.009557279967702925
|
||||
HashTable,отсортированный,поиск,1,9.810004848986864e-05
|
||||
HashTable,отсортированный,поиск,2,9.250000584870577e-05
|
||||
HashTable,отсортированный,поиск,3,9.019998833537102e-05
|
||||
HashTable,отсортированный,поиск,4,9.129999671131372e-05
|
||||
HashTable,отсортированный,поиск,5,9.280000813305378e-05
|
||||
HashTable,отсортированный,поиск,среднее,9.298000950366258e-05
|
||||
HashTable,отсортированный,удаление,1,4.429998807609081e-05
|
||||
HashTable,отсортированный,удаление,2,4.549999721348286e-05
|
||||
HashTable,отсортированный,удаление,3,4.339998122304678e-05
|
||||
HashTable,отсортированный,удаление,4,4.270009230822325e-05
|
||||
HashTable,отсортированный,удаление,5,4.349998198449612e-05
|
||||
HashTable,отсортированный,удаление,среднее,4.3880008161067965e-05
|
||||
BST,отсортированный,вставка,1,4.526228099945001
|
||||
BST,отсортированный,вставка,2,4.322803199989721
|
||||
BST,отсортированный,вставка,3,4.176126900012605
|
||||
BST,отсортированный,вставка,4,3.965669700060971
|
||||
BST,отсортированный,вставка,5,3.9622846000129357
|
||||
BST,отсортированный,вставка,среднее,4.190622500004247
|
||||
BST,отсортированный,поиск,1,0.030124699929729104
|
||||
BST,отсортированный,поиск,2,0.030757599975913763
|
||||
BST,отсортированный,поиск,3,0.03016249998472631
|
||||
BST,отсортированный,поиск,4,0.03018200001679361
|
||||
BST,отсортированный,поиск,5,0.030304200015962124
|
||||
BST,отсортированный,поиск,среднее,0.03030619998462498
|
||||
BST,отсортированный,удаление,1,0.016157799982465804
|
||||
BST,отсортированный,удаление,2,0.01620279997587204
|
||||
BST,отсортированный,удаление,3,0.017003200016915798
|
||||
BST,отсортированный,удаление,4,0.01792290003504604
|
||||
BST,отсортированный,удаление,5,0.017416900023818016
|
||||
BST,отсортированный,удаление,среднее,0.01694072000682354
|
||||
|
BIN
zverevem/lab1/docs/data/table_results.png
Normal file
BIN
zverevem/lab1/docs/data/table_results.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 128 KiB |
163
zverevem/lab1/docs/отчёт.ipynb
Normal file
163
zverevem/lab1/docs/отчёт.ipynb
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f058dc2c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Отчёт: Задание 1 — Структуры данных\n",
|
||||
"\n",
|
||||
"## Цель работы\n",
|
||||
"\n",
|
||||
"Разработать три структуры данных «с нуля» в процедурном стиле (без ООП), применить их для хранения записей телефонной книги и провести экспериментальное сравнение производительности ключевых операций.\n",
|
||||
"\n",
|
||||
"**Структуры данных:**\n",
|
||||
"- Связный список (LinkedList)\n",
|
||||
"- Хеш-таблица (HashTable)\n",
|
||||
"- Двоичное дерево поиска (BST)\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"## Реализация\n",
|
||||
"\n",
|
||||
"### Основные технические решения\n",
|
||||
"\n",
|
||||
"#### 1. Связный список\n",
|
||||
"\n",
|
||||
"Узел реализован как Python-словарь: `{'name': 'Имя', 'phone': '123', 'next': None}`.\n",
|
||||
"\n",
|
||||
"Новые элементы добавляются **в конец** списка за O(1) (без проверки на дубликаты имени). Поиск и удаление работают за линейное время O(n) из-за отсутствия прямого доступа по индексу.\n",
|
||||
"\n",
|
||||
"#### 2. Хеш-таблица\n",
|
||||
"\n",
|
||||
"Фиксированный массив на **1000 корзин**. Каждая корзина — указатель на связный список (метод цепочек). Хеш-функция: полиномиальная с основанием 31, свёрнутая по модулю размера таблицы. Среднее время операций O(1), при коллизиях — O(k), где k — длина цепочки.\n",
|
||||
"\n",
|
||||
"#### 3. Двоичное дерево поиска (BST)\n",
|
||||
"\n",
|
||||
"Узел: `{'name': 'Имя', 'phone': '123', 'left': None, 'right': None}`. Сравнение ключей — лексикографическое по полю `name`. Вставка и поиск реализованы итеративно. Удаление — с заменой на минимальный узел правого поддерева. Обход в глубину даёт отсортированный список.\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"## Экспериментальная часть\n",
|
||||
"\n",
|
||||
"### Условия проведения замеров\n",
|
||||
"\n",
|
||||
"| Параметр | Значение |\n",
|
||||
"|---|---|\n",
|
||||
"| Количество записей (N) | 10 000 |\n",
|
||||
"| Количество замеров на операцию | 5 |\n",
|
||||
"| Поисковых запросов | 110 (100 существующих + 10 отсутствующих) |\n",
|
||||
"| Удалений | 50 |\n",
|
||||
"| Размер хеш-таблицы | 1000 корзин |\n",
|
||||
"\n",
|
||||
"**Два набора данных:**\n",
|
||||
"- `records_shuffled` — случайный порядок записей\n",
|
||||
"- `records_sorted` — упорядоченный по имени (алфавитный порядок)\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"## Результаты\n",
|
||||
"\n",
|
||||
"### Среднее время выполнения (секунды)\n",
|
||||
"\n",
|
||||
"| Структура | Режим | Вставка (10000) | Поиск (110) | Удаление (50) |\n",
|
||||
"|-----------|-------|----------------|-------------|---------------|\n",
|
||||
"| LinkedList | случайный | 0.001383 | 0.00000514 | 0.00000234 |\n",
|
||||
"| LinkedList | отсортированный | 0.001167 | 0.00000500 | 0.00000230 |\n",
|
||||
"| HashTable | случайный | 0.010394 | 0.00009380 | 0.00004332 |\n",
|
||||
"| HashTable | отсортированный | 0.009557 | 0.00009298 | 0.00004388 |\n",
|
||||
"| BST | случайный | 0.015142 | 0.00013682 | 0.00007188 |\n",
|
||||
"| **BST** | **отсортированный** | **4.19062** | **0.030306** | **0.016941** |\n",
|
||||
"\n",
|
||||
"### Визуализация\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"## Анализ результатов\n",
|
||||
"\n",
|
||||
"### 1. Связный список — сверхбыстрая вставка, но линейный поиск\n",
|
||||
"\n",
|
||||
"- **Вставка** выполняется за **~0.0012 с** на 10000 элементов, так как добавление происходит в конец списка без проверки дубликатов (O(1) на операцию).\n",
|
||||
"- **Поиск** и **удаление** в замерах показали микросекунды, но это следствие малого количества операций (110 поисков, 50 удалений) и того, что искомые записи находятся в начале списка. В худшем случае (поиск отсутствующего элемента) сложность остаётся O(n).\n",
|
||||
"\n",
|
||||
"**Вывод:** связный список эффективен только при очень малых объёмах данных или когда вставка — единственная частая операция.\n",
|
||||
"\n",
|
||||
"### 2. Хеш-таблица — стабильная производительность\n",
|
||||
"\n",
|
||||
"Хеш-таблица демонстрирует **устойчивость к порядку входных данных**:\n",
|
||||
"- Вставка: ~0.010 с (быстрее BST на случайных данных, но медленнее LinkedList)\n",
|
||||
"- Поиск: ~0.000094 с (в 18 раз быстрее BST на случайных)\n",
|
||||
"- Удаление: ~0.000043 с (в 1.6 раза быстрее BST)\n",
|
||||
"\n",
|
||||
"Размер таблицы (1000 корзин) обеспечивает равномерное распределение ключей, поэтому производительность остаётся стабильной.\n",
|
||||
"\n",
|
||||
"### 3. BST катастрофически деградирует на упорядоченных данных\n",
|
||||
"\n",
|
||||
"Самый показательный результат эксперимента:\n",
|
||||
"\n",
|
||||
"| Операция | Случайный порядок | Отсортированный порядок | Ухудшение |\n",
|
||||
"|----------|------------------|------------------------|-----------|\n",
|
||||
"| Вставка | 0.01514 с | **4.19062 с** | **×277** |\n",
|
||||
"| Поиск | 0.0001368 с | **0.03031 с** | **×221** |\n",
|
||||
"| Удаление | 0.0000719 с | **0.01694 с** | **×236** |\n",
|
||||
"\n",
|
||||
"**Причина:** при вставке отсортированных данных дерево вырождается в линейный список — каждый новый элемент больше предыдущего и помещается только в правую ветку. Высота дерева становится O(n) вместо O(log n), что превращает все операции в линейные.\n",
|
||||
"\n",
|
||||
"### 4. Сравнение с теоретическими ожиданиями\n",
|
||||
"\n",
|
||||
"| Структура | Теоретическая вставка | Фактическая (случ./сорт.) | \n",
|
||||
"|-----------|----------------------|---------------------------|\n",
|
||||
"| LinkedList | O(1) (в конец) | 0.0014 с / 0.0012 с |\n",
|
||||
"| HashTable | O(1) в среднем | 0.0104 с / 0.0096 с |\n",
|
||||
"| BST | O(log n) в среднем | 0.0151 с (случ.) |\n",
|
||||
"| BST | O(n) в худшем | 4.19 с (сорт.) |\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"## Выводы и практические рекомендации\n",
|
||||
"\n",
|
||||
"### Выбор структуры в зависимости от задачи\n",
|
||||
"\n",
|
||||
"| Сценарий | Рекомендация |\n",
|
||||
"|----------|--------------|\n",
|
||||
"| **Частый поиск по ключу** | HashTable (быстрее всего) |\n",
|
||||
"| **Данные поступают упорядоченно** | HashTable (BST непригоден) |\n",
|
||||
"| **Только вставка и редкий поиск** | LinkedList (самая быстрая вставка) |\n",
|
||||
"| **Требуется отсортированный вывод** | BST (обход даёт порядок за O(n)) |\n",
|
||||
"| **Сбалансированные операции** | HashTable |\n",
|
||||
"| **Диапазонные запросы** (например, А–М) | BST (при условии балансировки) |\n",
|
||||
"\n",
|
||||
"### Теоретическая сложность операций \n",
|
||||
"| Структура | Insert | Find | Delete | Обход (отсорт.) |\n",
|
||||
"|-----------|--------|------|--------|-----------------|\n",
|
||||
"| LinkedList (в конец) | O(1) | O(n) | O(n) | O(n log n) |\n",
|
||||
"| HashTable | O(1) | O(1) | O(1) | O(n log n) |\n",
|
||||
"| BST (сбалансированный) | O(log n) | O(log n) | O(log n) | O(n) |\n",
|
||||
"| BST (вырожденный) | O(n) | O(n) | O(n) | O(n) |\n",
|
||||
"\n",
|
||||
"### Ключевой вывод\n",
|
||||
"\n",
|
||||
"Для телефонного справочника с частыми поисками и обновлениями оптимальный выбор — **хеш-таблица**. Она обеспечивает предсказуемую скорость вне зависимости от порядка данных.\n",
|
||||
"\n",
|
||||
"Обычный **связный список** полезен только как вспомогательная структура (например, для цепочек в хеш-таблице) или при минимальном объёме данных.\n",
|
||||
"\n",
|
||||
"**Двоичное дерево поиска** без самобалансировки опасно использовать с реальными данными, которые часто бывают частично или полностью упорядоченными. В таких случаях необходимо применять AVL или красно-чёрные деревья.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user