import sys sys.setrecursionlimit(10000) import time import random import csv import os import matplotlib.pyplot as plt import numpy as np def ll_insert(head, name, phone): new_node = {'name': name, 'phone': phone, 'next': None} if head is None: return new_node if head['name'] > name: new_node['next'] = head return new_node current = head while current['next'] is not None and current['next']['name'] < name: current = current['next'] if current['name'] == name: current['phone'] = phone return head new_node['next'] = 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): result = [] current = head while current is not None: result.append((current['name'], current['phone'])) current = current['next'] result.sort(key=lambda x: x[0]) return result def ht_insert(buckets, name, phone): index = hash(name) % len(buckets) buckets[index] = ll_insert(buckets[index], name, phone) def ht_find(buckets, name): index = hash(name) % len(buckets) return ll_find(buckets[index], name) def ht_delete(buckets, name): index = hash(name) % len(buckets) buckets[index] = ll_delete(buckets[index], name) def ht_list_all(buckets): result = [] for bucket in buckets: if bucket is not None: current = bucket while current is not None: result.append((current['name'], current['phone'])) current = current['next'] result.sort(key=lambda x: x[0]) return result 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): 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_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'] is not None: 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 is not None: inorder(node['left']) result.append((node['name'], node['phone'])) inorder(node['right']) inorder(root) return result def generate_records(n, mode='shuffled'): names = [f"User_{i:05d}" for i in range(n)] if mode == 'shuffled': random.shuffle(names) records = [(name, f"Phone_{i}") for i, name in enumerate(names)] return records def benchmark_insert(structure_type, records, buckets_size=512): if structure_type == 'LinkedList': head = None start = time.perf_counter() for name, phone in records: head = ll_insert(head, name, phone) end = time.perf_counter() return end - start, head elif structure_type == 'HashTable': buckets = [None] * buckets_size start = time.perf_counter() for name, phone in records: ht_insert(buckets, name, phone) end = time.perf_counter() return end - start, buckets elif structure_type == 'BST': root = None start = time.perf_counter() for name, phone in records: root = bst_insert(root, name, phone) end = time.perf_counter() return end - start, root def benchmark_find(structure_type, structure, names): start = time.perf_counter() if structure_type == 'LinkedList': for name in names: ll_find(structure, name) elif structure_type == 'HashTable': for name in names: ht_find(structure, name) elif structure_type == 'BST': for name in names: bst_find(structure, name) end = time.perf_counter() return end - start def benchmark_delete(structure_type, structure, names): start = time.perf_counter() if structure_type == 'LinkedList': head = structure for name in names: head = ll_delete(head, name) end = time.perf_counter() return end - start, head elif structure_type == 'HashTable': buckets = structure for name in names: ht_delete(buckets, name) end = time.perf_counter() return end - start, buckets elif structure_type == 'BST': root = structure for name in names: root = bst_delete(root, name) end = time.perf_counter() return end - start, root def benchmark_list_all(structure_type, structure): start = time.perf_counter() if structure_type == 'LinkedList': ll_list_all(structure) elif structure_type == 'HashTable': ht_list_all(structure) elif structure_type == 'BST': bst_list_all(structure) end = time.perf_counter() return end - start def main(): random.seed(42) N = 1000 REPETITIONS = 5 all_results = [] structures = ['LinkedList', 'HashTable', 'BST'] modes = ['shuffled', 'sorted'] os.makedirs('docs/data', exist_ok=True) for mode in modes: records = generate_records(N, mode) find_names = [random.choice(records)[0] for _ in range(100)] + [f"None_{i}" for i in range(10)] delete_names = [random.choice(records)[0] for _ in range(50)] for struct in structures: print(f"Тестирую {struct} на {mode} данных...") for rep in range(REPETITIONS): insert_time, structure = benchmark_insert(struct, records) all_results.append([struct, mode, 'insert', insert_time, rep + 1]) find_time = benchmark_find(struct, structure, find_names) all_results.append([struct, mode, 'find', find_time, rep + 1]) delete_time, _ = benchmark_delete(struct, structure, delete_names) all_results.append([struct, mode, 'delete', delete_time, rep + 1]) with open('docs/data/results.csv', 'w', newline='') as f: writer = csv.writer(f) writer.writerow(['structure', 'order', 'operation', 'time', 'run']) writer.writerows(all_results) print("\nCSV сохранён в docs/data/results.csv") data = {} for row in all_results: key = (row[0], row[1], row[2]) if key not in data: data[key] = [] data[key].append(row[3]) averaged = [] for key, times in data.items(): avg_time = sum(times) / len(times) averaged.append([key[0], key[1], key[2], avg_time]) print("\nУСРЕДНЁННЫЕ РЕЗУЛЬТАТЫ (сек):") print(f"{'Структура':<15} {'Режим':<15} {'Операция':<10} {'Среднее время':<15}") print("-" * 55) for row in sorted(averaged, key=lambda x: (x[0], x[1], x[2])): print(f"{row[0]:<15} {row[1]:<15} {row[2]:<10} {row[3]:<15.6f}") structures_list = ['LinkedList', 'HashTable', 'BST'] operations_list = ['insert', 'find', 'delete'] fig, axes = plt.subplots(1, 3, figsize=(18, 6)) for i, op in enumerate(operations_list): ax = axes[i] x = np.arange(len(structures_list)) width = 0.35 shuffled_times = [] sorted_times = [] for struct in structures_list: shuffled_times.append( next(row[3] for row in averaged if row[0] == struct and row[1] == 'shuffled' and row[2] == op)) sorted_times.append( next(row[3] for row in averaged if row[0] == struct and row[1] == 'sorted' and row[2] == op)) bars1 = ax.bar(x - width / 2, shuffled_times, width, label='Случайный', color='#3498db') bars2 = ax.bar(x + width / 2, sorted_times, width, label='Отсортированный', color='#e74c3c') for bar in bars1: height = bar.get_height() ax.text(bar.get_x() + bar.get_width() / 2., height + max(shuffled_times) * 0.01, f'{height:.6f}', ha='center', va='bottom', fontsize=7) for bar in bars2: height = bar.get_height() ax.text(bar.get_x() + bar.get_width() / 2., height + max(sorted_times) * 0.01, f'{height:.6f}', ha='center', va='bottom', fontsize=7) ax.set_xlabel('Структура данных') ax.set_ylabel('Время (сек)') ax.set_title(f'Операция: {op}') ax.set_xticks(x) ax.set_xticklabels(structures_list) ax.legend() ax.grid(axis='y', alpha=0.3) if op == 'find': ax.set_yscale('log') plt.suptitle(f'Сравнение производительности структур данных (N = {N} записей)', fontsize=14, fontweight='bold') plt.tight_layout() os.makedirs('docs', exist_ok=True) plt.savefig('docs/benchmark_results.png', dpi=150, bbox_inches='tight') plt.show() print(f"График сохранён в docs/benchmark_results.png") print("\nАНАЛИЗ РЕЗУЛЬТАТОВ") print("\n1. Влияние порядка данных на BST:") bst_shuffled_insert = next( row[3] for row in averaged if row[0] == 'BST' and row[1] == 'shuffled' and row[2] == 'insert') bst_sorted_insert = next( row[3] for row in averaged if row[0] == 'BST' and row[1] == 'sorted' and row[2] == 'insert') print(f" - Случайные данные: {bst_shuffled_insert:.6f} сек") print(f" - Отсортированные данные: {bst_sorted_insert:.6f} сек") if bst_shuffled_insert > 0: print(f" - Замедление в {bst_sorted_insert / bst_shuffled_insert:.1f} раз") print(" Причина: на отсортированных данных BST вырождается в связный список (глубина = N)") print("\n2. Стабильность хеш-таблицы:") ht_shuffled = next( row[3] for row in averaged if row[0] == 'HashTable' and row[1] == 'shuffled' and row[2] == 'insert') ht_sorted = next(row[3] for row in averaged if row[0] == 'HashTable' and row[1] == 'sorted' and row[2] == 'insert') print(f" - Случайные: {ht_shuffled:.6f} сек") print(f" - Отсортированные: {ht_sorted:.6f} сек") print(" Причина: хеш-функция равномерно распределяет ключи независимо от порядка") print("\n3. Медленный поиск в связном списке:") ll_search = next(row[3] for row in averaged if row[0] == 'LinkedList' and row[1] == 'shuffled' and row[2] == 'find') ht_search = next(row[3] for row in averaged if row[0] == 'HashTable' and row[1] == 'shuffled' and row[2] == 'find') print(f" - LinkedList: {ll_search:.6f} сек") print(f" - HashTable: {ht_search:.6f} сек") if ht_search > 0: print(f" - Хеш-таблица быстрее в {ll_search / ht_search:.1f} раз") print(" Причина: поиск в списке всегда O(n), в хеш-таблице ~O(1)") print("\n4. Удаление:") for label in ['LinkedList', 'HashTable', 'BST']: del_shuff = next(row[3] for row in averaged if row[0] == label and row[1] == 'shuffled' and row[2] == 'delete') del_sort = next(row[3] for row in averaged if row[0] == label and row[1] == 'sorted' and row[2] == 'delete') print(f" - {label:15}: случ.={del_shuff:.6f} сек, отсорт.={del_sort:.6f} сек") print("\n5. Рекомендации:") print(" - Частый поиск + вставки → Хеш-таблица") print(" - Нужна сортировка «из коробки» → Сбалансированное BST (AVL/Красно-чёрное)") print(" - Только добавление в конец → Связный список") print(" - Обычный BST опасен на реальных частично упорядоченных данных!") if __name__ == '__main__': main()