# Отчет по заданию 1: структуры данных ## Цель Реализовать три структуры данных с нуля в процедурной парадигме и сравнить скорость основных операций телефонного справочника: - `insert(name, phone)` - добавить или обновить запись; - `find(name)` - найти телефон по имени; - `delete(name)` - удалить запись; - `list_all()` - получить все записи, отсортированные по имени. Классы не использовались. Узлы связного списка и дерева представлены словарями, хеш-таблица представлена списком бакетов. ## Реализация Код находится в файле `phonebook.py`. Реализованы функции: - связный список: `ll_insert`, `ll_find`, `ll_delete`, `ll_list_all`; - хеш-таблица: `create_hash_table`, `ht_insert`, `ht_find`, `ht_delete`, `ht_list_all`; - двоичное дерево поиска: `bst_insert`, `bst_find`, `bst_delete`, `bst_list_all`. Для хеш-таблицы используется метод цепочек: каждый бакет хранит голову связного списка. Хеш-функция написана вручную, чтобы результат не зависел от рандомизации встроенной функции `hash()` в Python. Для BST вставка, поиск, удаление и обход написаны без классов. Обход `bst_list_all` реализован итеративно, чтобы отсортированный вход на 10000 элементов не приводил к переполнению стека рекурсии. ## Методика эксперимента Скрипт эксперимента находится в файле `benchmark.py`. Параметры запуска: - количество записей: `N = 10000`; - число повторов каждого эксперимента: `5`; - имена: `User_00000`, `User_00001`, ..., `User_09999`; - два режима входных данных: `shuffled` и `sorted`; - поиск: 100 существующих имен и 10 отсутствующих; - удаление: 50 случайных существующих имен; - размер хеш-таблицы: `20011` бакетов. После вставки структура не пересоздается: поиск и удаление выполняются на той же заполненной структуре. Для каждого режима и каждой структуры создается новая структура. Файлы с результатами: - `docs/data/results.csv` - все отдельные замеры; - `docs/data/summary.csv` - среднее время и список всех пяти замеров; - `docs/data/performance.svg` - столбчатая диаграмма средних значений. ![График производительности](data/performance.svg) ## Средние результаты Время указано в секундах. | Структура | Режим | Вставка | Поиск | Удаление | |---|---:|---:|---:|---:| | LinkedList | shuffled | 1.555942 | 0.014180 | 0.006345 | | LinkedList | sorted | 1.513740 | 0.012993 | 0.006041 | | HashTable | shuffled | 0.005934 | 0.000056 | 0.000031 | | HashTable | sorted | 0.006140 | 0.000056 | 0.000029 | | BST | shuffled | 0.011223 | 0.000093 | 0.000069 | | BST | sorted | 2.250442 | 0.019857 | 0.010803 | ## Анализ Связный список оказался самым медленным на вставке и поиске. Причина в том, что для корректной операции `insert` нужно проверить, есть ли уже запись с таким именем. При уникальных именах почти каждая вставка проходит по всему текущему списку, поэтому суммарная сложность вставки всех записей становится `O(n^2)`. Порядок входных данных почти не влияет на результат, потому что структура не использует порядок ключей. Хеш-таблица показала лучшие результаты почти во всех операциях. При хорошем распределении по бакетам вставка, поиск и удаление близки к `O(1)`. Порядок входных данных почти не влияет на время, так как индекс бакета определяется хешем имени, а не расположением записи во входном списке. BST хорошо работает на перемешанных данных: дерево получается сравнительно сбалансированным, поэтому операции близки к `O(log n)`. На отсортированном входе обычное двоичное дерево поиска вырождается в цепочку: каждый новый ключ становится правым потомком предыдущего. Из-за этого вставка всех записей становится `O(n^2)`, а поиск и удаление приближаются к поведению связного списка. Удаление у хеш-таблицы быстрое по той же причине, что и поиск: сначала вычисляется бакет, затем просматривается короткая цепочка. В BST удаление быстрое на перемешанном дереве, но на вырожденном дереве оно замедляется. В связном списке удаление требует линейного поиска удаляемого элемента. ## Вывод Для частого поиска, обновления и удаления по точному имени лучше выбирать хеш-таблицу. Она быстрее всего в эксперименте и почти не зависит от порядка вставки. Если нужно часто получать данные в отсортированном порядке, дерево поиска дает удобный `in-order` обход без отдельной сортировки. Но обычный BST чувствителен к порядку входных данных, поэтому на практике лучше использовать самобалансирующееся дерево или готовую структуру из библиотеки. Связный список подходит только для маленьких наборов данных или учебных задач. Для телефонного справочника с частым поиском он неудачен, потому что каждая операция поиска требует последовательного прохода по элементам.