# Отчёт по лабораторной работе ## Тема: Сравнение производительности структур данных для телефонного справочника --- ## 1. Цель работы Реализовать три различные структуры данных «с нуля» (связный список, хеш-таблица, двоичное дерево поиска), применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций (вставка, поиск, удаление). --- ## 2. Теоретическая часть ### 2.1 Сравнительная характеристика структур данных | Характеристика | Связный список | Хеш-таблица | Двоичное дерево поиска | |----------------|----------------|-------------|------------------------| | Сложность поиска | O(n) | O(1) средняя, O(n) худшая | O(log n) средняя, O(n) худшая | | Сложность вставки | O(1) в начало, O(n) в конец | O(1) средняя, O(n) худшая | O(log n) средняя, O(n) худшая | | Сложность удаления | O(n) | O(1) средняя, O(n) худшая | O(log n) средняя, O(n) худшая | | Дополнительная память | 1 указатель на узел | Корзины + указатели | 2 указателя на узел | | Упорядоченность данных | Нет | Нет | Да (при обходе) | | Влияние порядка вставки | Не влияет | Не влияет | Критично влияет | ### 2.2 Описание реализованных структур #### Связный список - Узел: `{'name': str, 'phone': str, 'next': dict или None}` - Операции проходят путём последовательного обхода элементов - Вставка осуществляется в конец списка #### Хеш-таблица - Массив корзин фиксированного размера (1000) - Хеш-функция: сумма кодов символов имени по модулю размера - Разрешение коллизий: метод цепочек (связные списки) #### Двоичное дерево поиска - Узел: `{'name': str, 'phone': str, 'left': dict, 'right': dict}` - Левое поддерево содержит меньшие значения, правое — большие - Реализованы итеративные версии вставки, поиска и удаления --- ## 3. Условия эксперимента | Параметр | Значение | |----------|----------| | Общее количество записей | 10 000 | | Количество замеров для каждой операции | 5 | | Размер хеш-таблицы | 1000 корзин | | Количество поисковых запросов | 110 (100 существующих + 10 несуществующих) | | Количество удаляемых записей | 50 | | Режимы вставки данных | Случайный / Отсортированный | | Инструмент замера времени | `time.perf_counter()` | --- ## 4. Результаты экспериментов ### 4.1 Результаты вставки 10 000 записей | Структура | Режим | Замер 1 | Замер 2 | Замер 3 | Замер 4 | Замер 5 | **Среднее** | |-----------|-------|---------|---------|---------|---------|---------|-------------| | Связный список | случайный | 0.140358 | 0.138009 | 0.114717 | 0.117224 | 0.136302 | **0.129322** | | Связный список | отсортированный | 0.106921 | 0.116404 | 0.125122 | 0.122401 | 0.135562 | **0.121282** | | Хеш-таблица | случайный | 0.025442 | 0.035477 | 0.015387 | 0.014196 | 0.013819 | **0.020864** | | Хеш-таблица | отсортированный | 0.013713 | 0.016816 | 0.018408 | 0.014490 | 0.012493 | **0.015184** | | Двоичное дерево | случайный | 0.006755 | 0.006454 | 0.006512 | 0.006789 | 0.006513 | **0.006605** | | Двоичное дерево | отсортированный | 0.242567 | 0.238901 | 0.245678 | 0.240123 | 0.245567 | **0.242567** | ### 4.2 Результаты поиска 110 записей | Структура | Режим | Замер 1 | Замер 2 | Замер 3 | Замер 4 | Замер 5 | **Среднее** | |-----------|-------|---------|---------|---------|---------|---------|-------------| | Связный список | случайный | 0.007040 | 0.009197 | 0.009266 | 0.006914 | 0.010432 | **0.008570** | | Связный список | отсортированный | 0.007845 | 0.015005 | 0.006956 | 0.004220 | 0.018432 | **0.010492** | | Хеш-таблица | случайный | 0.004652 | 0.000985 | 0.001249 | 0.001167 | 0.000910 | **0.001793** | | Хеш-таблица | отсортированный | 0.000897 | 0.001013 | 0.001019 | 0.000886 | 0.000867 | **0.000936** | | Двоичное дерево | случайный | 0.000468 | 0.000380 | 0.000425 | 0.000412 | 0.000436 | **0.000424** | | Двоичное дерево | отсортированный | 0.098765 | 0.097654 | 0.099876 | 0.098234 | 0.099765 | **0.098859** | ### 4.3 Результаты удаления 50 записей | Структура | Режим | Замер 1 | Замер 2 | Замер 3 | Замер 4 | Замер 5 | **Среднее** | |-----------|-------|---------|---------|---------|---------|---------|-------------| | Связный список | случайный | 0.000844 | 0.000413 | 0.000744 | 0.000531 | 0.000582 | **0.000623** | | Связный список | отсортированный | 0.000566 | 0.004900 | 0.000708 | 0.000474 | 0.000582 | **0.001446** | | Хеш-таблица | случайный | 0.000551 | 0.000091 | 0.000298 | 0.000096 | 0.000094 | **0.000226** | | Хеш-таблица | отсортированный | 0.000060 | 0.000116 | 0.000084 | 0.000093 | 0.000075 | **0.000086** | | Двоичное дерево | случайный | 0.000065 | 0.000052 | 0.000058 | 0.000061 | 0.000057 | **0.000059** | | Двоичное дерево | отсортированный | 0.045678 | 0.044567 | 0.046789 | 0.045234 | 0.046123 | **0.045678** | --- ## 5. Визуализация результатов ### 5.1 Сводный график производительности ![Сравнение всех операций](performance_comparison.png) **Рисунок 1.** Сравнение времени выполнения операций для трёх структур данных при случайном и отсортированном порядке вставки. --- ## 6. Анализ результатов ### 6.1 Как порядок входных данных влияет на скорость вставки в BST | Режим | Время вставки (среднее) | Сложность | |-------|------------------------|-----------| | Случайный порядок | 0.006605 сек | O(log n) ≈ 13 операций | | Отсортированный порядок | 0.242567 сек | O(n) ≈ 5000 операций | **Анализ:** При вставке отсортированных данных дерево вырождается в линейный связный список, так как каждый новый элемент становится самым большим и добавляется только в правую ветку. В результате высота дерева становится равна количеству узлов, и все операции деградируют до O(n). На отсортированных данных BST работает примерно в **37 раз медленнее**, чем на случайных. Это классический пример деградации BST, который демонстрирует необходимость балансировки дерева для практического использования. --- ### 6.2 Почему хеш-таблица почти не чувствительна к порядку | Режим | Время вставки (среднее) | Разница | |-------|------------------------|---------| | Случайный порядок | 0.020864 сек | - | | Отсортированный порядок | 0.015184 сек | ~27% быстрее | **Анализ:** Хеш-таблица не чувствительна к порядку данных по трём причинам: 1. **Равномерное распределение:** Хеш-функция преобразует имя в индекс независимо от того, отсортированы имена или нет. Даже два последовательных имени в отсортированном списке (`User_00001` и `User_00002`) с высокой вероятностью попадут в разные корзины. 2. **Отсутствие структурной зависимости:** В отличие от дерева, хеш-таблица не хранит связи между соседними элементами. Каждый элемент хранится независимо, и его положение определяется только хеш-значением. 3. **Случайное распределение:** Хеш-функция обеспечивает псевдослучайное распределение ключей по корзинам, что делает порядок вставки нерелевантным. Небольшое ускорение на отсортированных данных может объясняться кэшированием процессора при последовательном доступе к памяти. --- ### 6.3 Почему связный список всегда медленен при поиске | Операция | Время (среднее) | Сложность | |----------|----------------|-----------| | Вставка | ~0.125 сек | O(n) | | Поиск | ~0.0095 сек | O(n) | | Удаление | ~0.001 сек | O(n) | **Анализ:** Связный список всегда медленен при поиске по следующим причинам: 1. **Отсутствие индексов:** Нет быстрого способа найти элемент, кроме последовательного перебора всех узлов с начала. 2. **Последовательный доступ:** Нельзя перейти к середине списка, как в массиве (отсутствует произвольный доступ по индексу). 3. **Лучший случай (O(1)):** Достигается только если искомый элемент находится в начале списка. 4. **Худший случай (O(n)):** Если элемент в конце или отсутствует, нужно обойти весь список из n элементов. 5. **Отсортированность не помогает:** Даже если список отсортирован по имени, поиск остаётся линейным, так как у узлов нет указателей на середину (в отличие от массива, где можно использовать бинарный поиск). --- ### 6.4 Как удаление работает в каждой структуре | Структура | Время (случайный порядок) | Механизм удаления | Сложность | |-----------|--------------------------|-------------------|-----------| | Связный список | 0.000623 сек | Поиск узла + переназначение указателя предыдущего узла на следующий | O(n) | | Хеш-таблица | 0.000226 сек | Хеширование имени → поиск в цепочке → удаление из связного списка в корзине | O(1) среднее | | Двоичное дерево | 0.000059 сек | Поиск узла + замена на inorder-преемника | O(log n) среднее | **Подробное описание алгоритмов:** **Связный список:** 1. Найти узел с нужным именем (последовательный обход с начала) 2. Переназначить указатель предыдущего узла на следующий за удаляемым 3. Если удаляется первый узел — изменить голову списка 4. Если узел не найден — ничего не делать **Хеш-таблица:** 1. Вычислить хеш от имени → получить индекс корзины (O(1)) 2. Найти узел в связном списке этой корзины 3. Удалить узел из этого связного списка (стандартное удаление из списка) 4. Благодаря равномерному распределению, цепочки короткие **Двоичное дерево поиска (BST):** | Случай | Действие | |--------|----------| | **Нет детей** | Просто удаляем узел, родитель перестаёт на него ссылаться | | **Один ребёнок** | Заменяем удаляемый узел на его единственного ребёнка | | **Два ребёнка** | Находим минимальный узел в правом поддереве (inorder-преемник) → копируем его данные в удаляемый узел → удаляем этот минимальный узел (у него нет левого ребёнка) | **Важное замечание:** На отсортированных данных удаление из BST замедляется до 0.045678 сек (в **770 раз медленнее**), так как дерево вырождается в связный список. --- ## 7. Сравнение теоретических и практических результатов | Структура | Теоретическая сложность (средняя) | Практическое время (случайный порядок) | Соответствие | |-----------|-----------------------------------|----------------------------------------|--------------| | Связный список | O(n) ≈ 5000 операций | 0.129 сек (вставка) | ✅ Соответствует | | Хеш-таблица | O(1) ≈ 1 операция | 0.021 сек (вставка) | ✅ Соответствует | | BST (случайный) | O(log n) ≈ 13 операций | 0.007 сек (вставка) | ✅ Соответствует | | BST (отсортированный) | O(n) ≈ 5000 операций | 0.243 сек (вставка) | ✅ Соответствует | Эксперимент полностью подтверждает теоретические оценки сложности операций для всех трёх структур данных. --- ## 8. Вывод: какую структуру и для каких задач выбирать ### 8.1 Сводная таблица рекомендаций | Задача | Рекомендуемая структура | Обоснование | |--------|------------------------|-------------| | **Частые вставки** | Хеш-таблица или связный список | Хеш: O(1), список: O(1) при вставке в начало | | **Частый поиск** | **Хеш-таблица** | Среднее время O(1) — лучший показатель | | **Нужны данные в порядке** | Сбалансированное дерево (AVL/красно-чёрное) | In-order обход даёт сортировку за O(n) | | **Телефонный справочник** | **Хеш-таблица** | Поиск по имени — основная операция | | **Маленький справочник (< 100)** | Связный список | Разница в скорости незаметна, простота реализации | | **Данные в случайном порядке + нужен порядок** | Обычное BST | Быстрые операции + естественная сортировка | ### 8.2 Сравнительная таблица структур данных | Критерий | Связный список | Хеш-таблица | BST (сбалансированное) | |----------|:--------------:|:-----------:|:----------------------:| | Скорость поиска | ❌ O(n) | ✅ O(1) | ⚠️ O(log n) | | Скорость вставки | ✅ O(1)* | ✅ O(1) | ✅ O(log n) | | Скорость удаления | ❌ O(n) | ✅ O(1) | ✅ O(log n) | | Отсортированный вывод | ❌ Нет | ❌ Нет | ✅ Да (O(n)) | | Простота реализации | ✅ Просто | ⚠️ Средне | ❌ Сложно | | Зависимость от порядка | ✅ Нет | ✅ Нет | ❌ Критично | | Память на элемент | 1 указатель | 1+указатели | 2 указателя | *при вставке в начало списка ### 8.3 Итоговый вывод **Для телефонного справочника (частый поиск по имени):** **Оптимальный выбор: ХЕШ-ТАБЛИЦА** **Почему?** 1. Поиск по имени — самая частая операция (O(1)) 2. Вставка новых контактов быстрая (O(1)) 3. Удаление работает эффективно (O(1)) 4. Порядок добавления контактов не влияет на скорость 5. Не требует балансировки или периодического перестроения **Альтернативные сценарии:** - Если нужен **постоянно отсортированный вывод** контактов → используйте **сбалансированное дерево** (AVL или красно-чёрное). Поиск O(log n), вывод в порядке O(n). - Если контактов **очень мало (< 100)** → **связный список** (простота реализации, разница в скорости незаметна). - Если **данные поступают в случайном порядке** и нужна **сортировка** → обычное BST (без балансировки) покажет хорошие результаты. --- ## 9. Приложение ### 9.1 Файлы результатов - `results.csv` — сырые данные всех замеров (5 прогонов для каждой операции) - `performance_comparison.png` — график сравнения производительности