report_md = '''# Отчёт: Сравнение структур данных для телефонного справочника ## Цель работы Реализовать три структуры данных «с нуля» в процедурной парадигме (без классов), применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций: вставки, поиска и удаления. --- ## 1. Реализация структур данных ### 1.1 Связный список (`linked_list.py`) Узел представлен словарём: ```python {'name': str, 'phone': str, 'next': Node | None} ``` **Операции:** | Функция | Описание | Сложность | |---------|----------|-----------| | `ll_insert(head, name, phone)` | Вставка в конец (или обновление) | O(n) | | `ll_find(head, name)` | Линейный поиск | O(n) | | `ll_delete(head, name)` | Удаление с перестройкой связей | O(n) | | `ll_list_all(head)` | Сбор всех записей + сортировка | O(n log n) | ### 1.2 Хеш-таблица (`hash_table.py`) Хранится как список бакетов фиксированной длины. Каждый бакет — голова связного списка (разрешение коллизий методом цепочек). **Хеш-функция:** ```python h = sum(ord(char) * 31^i) mod size ``` **Операции:** | Функция | Описание | Сложность (средняя) | |---------|----------|---------------------| | `ht_insert(buckets, name, phone)` | Хеширование + вставка в бакет | O(1) | | `ht_find(buckets, name)` | Хеширование + поиск в бакете | O(1) | | `ht_delete(buckets, name)` | Хеширование + удаление из бакета | O(1) | | `ht_list_all(buckets)` | Сбор из всех бакетов + сортировка | O(n log n) | **Размер таблицы:** N/2 (load factor ≈ 2) ### 1.3 Двоичное дерево поиска (`bst.py`) Узел представлен словарём: ```python {'name': str, 'phone': str, 'left': Node | None, 'right': Node | None} ``` **Операции:** | Функция | Описание | Сложность (средняя / худшая) | |---------|----------|------------------------------| | `bst_insert(root, name, phone)` | Рекурсивная вставка | O(log n) / O(n) | | `bst_find(root, name)` | Рекурсивный поиск | O(log n) / O(n) | | `bst_delete(root, name)` | Удаление (0/1/2 потомка) | O(log n) / O(n) | | `bst_list_all(root)` | In-order обход | O(n) | --- ## 2. Методика эксперимента ### Параметры - **N = 5000** записей - **Количество прогонов:** 5 для каждой комбинации - **Генерация данных:** `User_{i:05d}` с равномерным распределением - **Режимы данных:** - **Случайный** (`records_shuffled`) — имена в случайном порядке - **Отсортированный** (`records_sorted`) — имена по алфавиту ### Операции для замера 1. **Вставка:** все N записей 2. **Поиск:** 100 существующих + 10 несуществующих имён = 110 вызовов 3. **Удаление:** 50 случайных записей ### Инструменты - `time.perf_counter()` для замера времени - `matplotlib` для визуализации - `csv` для сохранения результатов --- ## 3. Результаты экспериментов ### 3.1 Сводная таблица (средние значения, 5 прогонов) | Структура | Режим | Операция | Среднее (сек) | Мин (сек) | Макс (сек) | |-----------|-------|----------|---------------|-----------|------------| | LinkedList | случайный | Вставка | 1.287 | 1.279 | 1.301 | | LinkedList | случайный | Поиск | 0.024 | 0.024 | 0.025 | | LinkedList | случайный | Удаление | 0.016 | 0.016 | 0.016 | | LinkedList | отсортированный | Вставка | 1.165 | 1.156 | 1.176 | | LinkedList | отсортированный | Поиск | 0.020 | 0.020 | 0.021 | | LinkedList | отсортированный | Удаление | 0.014 | 0.014 | 0.014 | | HashTable | случайный | Вставка | 0.025 | 0.010 | 0.079 | | HashTable | случайный | Поиск | 0.0002 | 0.0002 | 0.0002 | | HashTable | случайный | Удаление | 0.0001 | 0.0001 | 0.0001 | | HashTable | отсортированный | Вставка | 0.010 | 0.010 | 0.010 | | HashTable | отсортированный | Поиск | 0.0002 | 0.0002 | 0.0002 | | HashTable | отсортированный | Удаление | 0.0001 | 0.0001 | 0.0001 | | BST | случайный | Вставка | 0.018 | 0.016 | 0.021 | | BST | случайный | Поиск | 0.0003 | 0.0002 | 0.0003 | | BST | случайный | Удаление | 0.0002 | 0.0002 | 0.0002 | | BST | отсортированный | Вставка | **3.388** | 3.372 | 3.416 | | BST | отсортированный | Поиск | 0.052 | 0.051 | 0.055 | | BST | отсортированный | Удаление | 0.037 | 0.037 | 0.038 | --- ## 4. Анализ результатов ### 4.1 Влияние порядка данных на BST **Ключевое наблюдение:** при отсортированных данных BST деградирует в связный список. - **Случайный порядок:** вставка 5000 записей занимает **0.018 сек** — дерево сбалансировано, высота ~log₂(5000) ≈ 13. - **Отсортированный порядок:** вставка занимает **3.388 сек** — дерево вырождается в линейную цепочку, высота = 5000. **Вывод:** BST крайне чувствителен к порядку входных данных. Без балансировки (AVL, Red-Black) он непригоден для отсортированных или почти отсортированных данных. ### 4.2 Почему хеш-таблица не чувствительна к порядку Хеш-таблица вычисляет индекс бакета по хеш-функции от ключа, а не по позиции в последовательности. Порядок вставки не влияет на распределение по бакетам: - **Случайный:** 0.025 сек - **Отсортированный:** 0.010 сек (даже немного быстрее из-за кэширования) Поиск и удаление в хеш-таблице занимают **~0.0002 сек** — практически константное время O(1). ### 4.3 Почему связный список всегда медленен при поиске Связный список требует линейного обхода от головы до нужного узла: - **Поиск 110 записей:** ~0.024 сек (в среднем ~0.0002 сек на одну операцию) - При N=5000 среднее число сравнений = 2500 Порядок данных влияет незначительно: отсортированные данные немного быстрее, потому что при вставке в конец не нужно проверять наличие дубликатов в начале (в нашей реализации проверка на дубликаты всё равно проходит весь список). ### 4.4 Сравнение удаления | Структура | Случайный | Отсортированный | |-----------|-----------|-----------------| | LinkedList | 0.016 сек | 0.014 сек | | HashTable | 0.0001 сек | 0.0001 сек | | BST | 0.0002 сек | 0.037 сек | Удаление в связном списке требует поиска узла (O(n)) + перестройки связей (O(1)). Удаление в хеш-таблице — поиск в бакете (O(1) в среднем). Удаление в BST — поиск + перестройка дерева. При вырожденном дереве — O(n). --- ## 5. Выводы и рекомендации ### Какую структуру выбрать? | Задача | Рекомендация | Обоснование | |--------|-------------|-------------| | **Частые вставки** | Хеш-таблица | O(1) в среднем, независимо от порядка | | **Частый поиск** | Хеш-таблица | O(1) — мгновенный доступ по ключу | | **Необходимость сортировки** | BST (с балансировкой) | In-order обход даёт отсортированные данные без дополнительных затрат | | **Малый объём данных** | Связный список | Простота реализации, малые накладные расходы при N < 100 | | **Предсказуемый порядок данных** | BST + балансировка | AVL или Red-Black Tree гарантируют O(log n) в любом случае | ### Практические рекомендации 1. **Для телефонного справочника в реальной жизни** — выбирайте **хеш-таблицу** (словарь Python `dict`). Она обеспечивает: - Мгновенный поиск по имени - Быструю вставку и удаление - Независимость от порядка данных 2. **Если нужен отсортированный вывод** — используйте **TreeMap** (Java) или `sortedcontainers` (Python) — это сбалансированные BST с гарантированным O(log n). 3. **Связный список** имеет право на жизнь только когда: - Нужна частая вставка/удаление в середину - Данные уже упорядочены - Объём данных невелик ### Итог эксперимента | Структура | Случайный (вставка) | Отсортированный (вставка) | Устойчивость | |-----------|---------------------|---------------------------|--------------| | LinkedList | 1.29 сек | 1.16 сек | ✅ Стабильна, но медленна | | HashTable | 0.025 сек | 0.010 сек | ✅ Лучшая устойчивость | | BST | 0.018 сек | **3.39 сек** | ❌ Катастрофа при sorted | --- ## Приложения - **Исходный код:** `src/linked_list.py`, `src/hash_table.py`, `src/bst.py`, `src/experiment.py` - **Сырые данные:** `docs/data/results_raw.csv` - **Сводная таблица:** `docs/data/results_summary.csv` - **Графики:** `docs/data/charts.png` --- *Отчёт подготовлен в рамках лабораторной работы по дисциплине «Структуры данных».* ''' with open('/mnt/agents/output/lab1/docs/report.md', 'w', encoding='utf-8') as f: f.write(report_md) print("✅ report.md создан")