import time import csv import random import copy 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): """Возвращает телефон или None.""" 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 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 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 # Случай 1: нет детей 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 # Случай 2: один ребёнок if current['left'] is None: child = current['right'] elif current['right'] is None: child = current['left'] else: # Случай 3: два ребёнка 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 def generate_records(N=10000): """Возвращает (shuffled, sorted) списки записей.""" records = [(f"User_{i:05d}", f"phone_{i}") for i in range(N)] shuffled = copy.deepcopy(records) random.shuffle(shuffled) return shuffled, records def test_linked_list(records_shuffled, records_sorted, results): N = len(records_shuffled) # Вставка shuffled (5 повторов) 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) # Поиск (100 существующих + 10 несуществующих) 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) # Удаление 50 записей 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) 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) 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) def save_results(results, filename="docs/data/results.csv"): import os 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. 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) print("\nГотово! Файл results.csv создан.") print("Можешь построить графики в Excel или Google Sheets.") if __name__ == "__main__": main()