207 lines
12 KiB
Markdown
207 lines
12 KiB
Markdown
|
||
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 создан")
|