12 KiB
report_md = '''# Отчёт: Сравнение структур данных для телефонного справочника
Цель работы
Реализовать три структуры данных «с нуля» в процедурной парадигме (без классов), применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций: вставки, поиска и удаления.
1. Реализация структур данных
1.1 Связный список (linked_list.py)
Узел представлен словарём:
{'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)
Хранится как список бакетов фиксированной длины. Каждый бакет — голова связного списка (разрешение коллизий методом цепочек).
Хеш-функция:
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)
Узел представлен словарём:
{'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) — имена по алфавиту
- Случайный (
Операции для замера
- Вставка: все N записей
- Поиск: 100 существующих + 10 несуществующих имён = 110 вызовов
- Удаление: 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) в любом случае |
Практические рекомендации
-
Для телефонного справочника в реальной жизни — выбирайте хеш-таблицу (словарь Python
dict). Она обеспечивает:- Мгновенный поиск по имени
- Быструю вставку и удаление
- Независимость от порядка данных
-
Если нужен отсортированный вывод — используйте TreeMap (Java) или
sortedcontainers(Python) — это сбалансированные BST с гарантированным O(log n). -
Связный список имеет право на жизнь только когда:
- Нужна частая вставка/удаление в середину
- Данные уже упорядочены
- Объём данных невелик
Итог эксперимента
| Структура | Случайный (вставка) | Отсортированный (вставка) | Устойчивость |
|---|---|---|---|
| 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 создан")