import time import csv import random import copy import os # ============================================================ # 1. СВЯЗНЫЙ СПИСОК (LinkedList) # ============================================================ def ll_insert(head, name, phone): if head is None: return {'name': name, 'phone': phone, 'next': None} curr = head while curr is not None: if curr['name'] == name: curr['phone'] = phone return head curr = curr['next'] new_node = {'name': name, 'phone': phone, 'next': None} curr = head while curr['next'] is not None: curr = curr['next'] curr['next'] = new_node return head def ll_find(head, name): curr = head while curr is not None: 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'] prev = head curr = head['next'] while curr is not None: if curr['name'] == name: prev['next'] = curr['next'] return head prev = curr curr = curr['next'] return head def ll_list_all(head): records = [] curr = head while curr is not None: records.append((curr['name'], curr['phone'])) curr = curr['next'] records.sort(key=lambda x: x[0]) return records # ============================================================ # 2. ХЕШ-ТАБЛИЦА (HashTable) # ============================================================ def _hash(name, bucket_count): return sum(ord(ch) for ch in name) % bucket_count def ht_create(bucket_count=1000): return [None] * bucket_count def ht_insert(buckets, name, phone): idx = _hash(name, len(buckets)) buckets[idx] = ll_insert(buckets[idx], name, phone) def ht_find(buckets, name): idx = _hash(name, len(buckets)) return ll_find(buckets[idx], name) def ht_delete(buckets, name): idx = _hash(name, len(buckets)) buckets[idx] = ll_delete(buckets[idx], name) def ht_list_all(buckets): all_records = [] for head in buckets: curr = head while curr is not None: all_records.append((curr['name'], curr['phone'])) curr = curr['next'] all_records.sort(key=lambda x: x[0]) return all_records # ============================================================ # 3. ДВОИЧНОЕ ДЕРЕВО ПОИСКА (BST) — итеративная версия # ============================================================ 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_min_node(node): while node['left'] is not None: node = node['left'] return node def bst_delete(root, name): 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'] is 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'] is successor: successor_parent['left'] = successor['right'] else: successor_parent['right'] = successor['right'] return root if parent is None: return child if parent['left'] is current: parent['left'] = child else: parent['right'] = child return root def bst_list_all(root): result = [] stack = [] current = root while stack or current is not None: while current is not None: stack.append(current) current = current['left'] current = stack.pop() result.append((current['name'], current['phone'])) current = current['right'] return result # ============================================================ # 4. ГЕНЕРАЦИЯ ТЕСТОВЫХ ДАННЫХ # ============================================================ def generate_records(N=5000): records = [(f"User_{i:05d}", f"phone_{i}") for i in range(N)] shuffled = copy.deepcopy(records) random.shuffle(shuffled) return shuffled, records # ============================================================ # 5. ЗАМЕРЫ ДЛЯ LINKEDLIST # ============================================================ def test_linked_list(records_shuffled, records_sorted, results): N = len(records_shuffled) # Вставка shuffled times = [] for _ in range(5): head = None start = time.perf_counter() for name, phone in records_shuffled: head = ll_insert(head, name, phone) times.append(time.perf_counter() - start) results.append(["LinkedList", "shuffled", "insert", sum(times) / 5] + times) # Вставка sorted times = [] for _ in range(5): head = None start = time.perf_counter() for name, phone in records_sorted: head = ll_insert(head, name, phone) times.append(time.perf_counter() - start) results.append(["LinkedList", "sorted", "insert", sum(times) / 5] + times) # Подготовка для поиска/удаления head = None for name, phone in records_shuffled: head = ll_insert(head, name, phone) # Поиск existing = [f"User_{i:05d}" for i in random.sample(range(N), 100)] nonexisting = [f"None_{i}" for i in range(10)] search_names = existing + nonexisting times = [] for _ in range(5): start = time.perf_counter() for name in search_names: ll_find(head, name) times.append(time.perf_counter() - start) results.append(["LinkedList", "shuffled", "search", sum(times) / 5] + times) # Удаление delete_names = [f"User_{i:05d}" for i in random.sample(range(N), 50)] times = [] for _ in range(5): head_copy = None for name, phone in records_shuffled: head_copy = ll_insert(head_copy, name, phone) start = time.perf_counter() for name in delete_names: head_copy = ll_delete(head_copy, name) times.append(time.perf_counter() - start) results.append(["LinkedList", "shuffled", "delete", sum(times) / 5] + times) # ============================================================ # 6. ЗАМЕРЫ ДЛЯ ХЕШ-ТАБЛИЦЫ # ============================================================ def test_hash_table(records_shuffled, records_sorted, results): N = len(records_shuffled) bucket_count = 1000 # Вставка shuffled times = [] for _ in range(5): buckets = ht_create(bucket_count) start = time.perf_counter() for name, phone in records_shuffled: ht_insert(buckets, name, phone) times.append(time.perf_counter() - start) results.append(["HashTable", "shuffled", "insert", sum(times) / 5] + times) # Вставка sorted times = [] for _ in range(5): buckets = ht_create(bucket_count) start = time.perf_counter() for name, phone in records_sorted: ht_insert(buckets, name, phone) times.append(time.perf_counter() - start) results.append(["HashTable", "sorted", "insert", sum(times) / 5] + times) # Подготовка buckets = ht_create(bucket_count) for name, phone in records_shuffled: ht_insert(buckets, name, phone) # Поиск existing = [f"User_{i:05d}" for i in random.sample(range(N), 100)] nonexisting = [f"None_{i}" for i in range(10)] search_names = existing + nonexisting times = [] for _ in range(5): start = time.perf_counter() for name in search_names: ht_find(buckets, name) times.append(time.perf_counter() - start) results.append(["HashTable", "shuffled", "search", sum(times) / 5] + times) # Удаление delete_names = [f"User_{i:05d}" for i in random.sample(range(N), 50)] times = [] for _ in range(5): buckets_copy = ht_create(bucket_count) for name, phone in records_shuffled: ht_insert(buckets_copy, name, phone) start = time.perf_counter() for name in delete_names: ht_delete(buckets_copy, name) times.append(time.perf_counter() - start) results.append(["HashTable", "shuffled", "delete", sum(times) / 5] + times) # ============================================================ # 7. ЗАМЕРЫ ДЛЯ BST # ============================================================ def test_bst(records_shuffled, records_sorted, results): N = len(records_shuffled) # Вставка shuffled times = [] for _ in range(5): root = None start = time.perf_counter() for name, phone in records_shuffled: root = bst_insert(root, name, phone) times.append(time.perf_counter() - start) results.append(["BST", "shuffled", "insert", sum(times) / 5] + times) # Вставка sorted times = [] for _ in range(5): root = None start = time.perf_counter() for name, phone in records_sorted: root = bst_insert(root, name, phone) times.append(time.perf_counter() - start) results.append(["BST", "sorted", "insert", sum(times) / 5] + times) # Подготовка root = None for name, phone in records_shuffled: root = bst_insert(root, name, phone) # Поиск existing = [f"User_{i:05d}" for i in random.sample(range(N), 100)] nonexisting = [f"None_{i}" for i in range(10)] search_names = existing + nonexisting times = [] for _ in range(5): start = time.perf_counter() for name in search_names: bst_find(root, name) times.append(time.perf_counter() - start) results.append(["BST", "shuffled", "search", sum(times) / 5] + times) # Удаление delete_names = [f"User_{i:05d}" for i in random.sample(range(N), 50)] times = [] for _ in range(5): root_copy = None for name, phone in records_shuffled: root_copy = bst_insert(root_copy, name, phone) start = time.perf_counter() for name in delete_names: root_copy = bst_delete(root_copy, name) times.append(time.perf_counter() - start) results.append(["BST", "shuffled", "delete", sum(times) / 5] + times) # ============================================================ # 8. СОХРАНЕНИЕ РЕЗУЛЬТАТОВ В CSV # ============================================================ def save_results(results, filename="docs/data/results.csv"): os.makedirs("docs/data", exist_ok=True) with open(filename, "w", newline="", encoding="utf-8") as f: writer = csv.writer(f) writer.writerow(["Structure", "Mode", "Operation", "AvgSec", "Run1", "Run2", "Run3", "Run4", "Run5"]) for row in results: writer.writerow(row) print(f"Результаты сохранены в {filename}") # ============================================================ # 9. ПОСТРОЕНИЕ ГРАФИКОВ # ============================================================ def plot_results(): """Построение графиков по результатам из CSV""" try: import matplotlib.pyplot as plt import pandas as pd except ImportError: print("Библиотеки matplotlib или pandas не установлены. Пропускаем графики.") print("Установите: pip install matplotlib pandas") return try: df = pd.read_csv("docs/data/results.csv") except FileNotFoundError: print("Файл results.csv не найден. Сначала запустите main.py для генерации данных.") return operations = df["Operation"].unique() for op in operations: subset = df[df["Operation"] == op] plt.figure(figsize=(10, 6)) labels = [f"{row.Structure}\n({row.Mode})" for _, row in subset.iterrows()] values = subset["AvgSec"] plt.bar(labels, values, color=['blue', 'orange', 'green', 'red', 'purple', 'brown']) plt.title(f"Сравнение времени {op} (5 замеров, N=5000)") plt.ylabel("Время (секунды)") plt.xticks(rotation=45) plt.tight_layout() filename = f"docs/graph_{op}.png" plt.savefig(filename) print(f"Сохранён график: {filename}") plt.close() print("\nГрафики построены и сохранены в папке docs/") # ============================================================ # 10. MAIN # ============================================================ def main(): print("Генерация тестовых данных (N=5000)...") shuffled, sorted_records = generate_records(5000) results = [] print("Тестирование LinkedList...") test_linked_list(shuffled, sorted_records, results) print("Тестирование HashTable...") test_hash_table(shuffled, sorted_records, results) print("Тестирование BST...") test_bst(shuffled, sorted_records, results) save_results(results) # Построение графиков plot_results() print("\nГотово! Файл results.csv и графики сохранены в папке docs/") if __name__ == "__main__": main()