2026-rff_mp/MashinDD/lab1/docs/report.md
2026-05-11 16:40:10 +03:00

146 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Отчёт: Задание 1 — Структуры данных
## Цель работы
Реализовать три структуры данных «с нуля» в процедурной парадигме (без классов), применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций.
**Структуры данных:**
- Связный список (LinkedList)
- Хеш-таблица (HashTable)
- Двоичное дерево поиска (BST)
---
## Реализация
### Файловая структура
```
task1/
├── phone_book.py # все три структуры данных
├── benchmark.py # генерация данных + замеры
├── plot_results.py # построение графиков
└── docs/
├── report.md # этот отчёт
└── data/
├── results.csv
├── comparison_by_operation.png
└── sorted_vs_random.png
```
### Ключевые решения реализации
#### 1. Связный список
Узел — Python-словарь: `{'name': 'Имя', 'phone': '123', 'next': None}`.
Вставка добавляет **в начало** списка за O(1) (если имя не существует), а при обновлении — проходит по списку O(n). Поиск и удаление — всегда O(n), так как нет случайного доступа.
#### 2. Хеш-таблица
Массив из 256 бакетов. Каждый бакет — голова связного списка (цепочки для разрешения коллизий). Хеш-функция: стандартный `hash(name) % size`. Операции в среднем O(1), при коллизиях — O(k), где k — длина цепочки.
#### 3. Двоичное дерево поиска (BST)
Узел: `{'name': 'Имя', 'phone': '123', 'left': None, 'right': None}`. Ключ сравнения — имя лексикографически. Вставка и поиск итеративные. Удаление рекурсивное (замена минимальным узлом правого поддерева). In-order обход даёт отсортированный список.
---
## Экспериментальная часть
### Параметры эксперимента
| Параметр | Значение |
|---|---|
| Количество записей (N) | 10 000 |
| Повторений каждого замера | 5 |
| Поисковых запросов | 110 (100 существующих + 10 несуществующих) |
| Удалений | 50 |
| Размер хеш-таблицы | 256 бакетов |
**Два варианта входных данных:**
- `records_shuffled` — случайный порядок (перемешанные записи)
- `records_sorted` — отсортированный по имени (по алфавиту)
---
## Результаты
### Таблица средних времён (секунды)
| Структура | Режим | Вставка (с) | Поиск 110 (с) | Удаление 50 (с) |
|---|---|---|---|---|
| LinkedList | случайный | 2.541985 | 0.034289 | 0.020349 |
| LinkedList | сортированный | 2.208557 | 0.025340 | 0.016424 |
| HashTable | случайный | 0.018235 | 0.000214 | 0.000120 |
| HashTable | сортированный | 0.016163 | 0.000207 | 0.000124 |
| BST | случайный | 0.017192 | 0.000145 | 0.000104 |
| **BST** | **сортированный** | **3.854338** | **0.033498** | **0.045823** |
### Графики
![Сравнение по операциям](data/comparison_by_operation.png)
![Влияние порядка данных](data/sorted_vs_random.png)
---
## Анализ результатов
### 1. Связный список — всегда медленный поиск
Вставка в список занимает **~2.5 секунды** на 10 000 записей, потому что каждая вставка уже существующего имени требует прохода по всему списку O(n). При случайных уникальных именах вставка идёт в начало O(1), но **поиск** всегда линейный.
**Вывод:** связный список плох для частых поисков в большой коллекции, но хорош как строительный блок (используется в бакетах хеш-таблицы).
### 2. Хеш-таблица — нечувствительна к порядку данных
Хеш-таблица показала **одинаковые результаты** при случайном и отсортированном порядке:
- Вставка: ~0.017 с (в ~150 раз быстрее LinkedList)
- Поиск: ~0.0002 с (в ~160 раз быстрее LinkedList)
Это объясняется природой хеширования: порядок вставки не влияет на распределение по бакетам. Ключ всегда попадает в предсказуемый бакет за O(1).
### 3. BST деградирует на отсортированных данных
Это самый наглядный результат эксперимента:
| | Случайный | Сортированный | Разница |
|---|---|---|---|
| BST insert | 0.017 с | **3.854 с** | **×225** |
| BST find | 0.000145 с | **0.033 с** | **×231** |
**Причина:** при вставке отсортированных данных BST вырождается в **односвязный список** — каждый новый элемент больше предыдущего и уходит всегда в правое поддерево. Высота дерева становится O(n) вместо O(log n). Поиск и удаление тоже деградируют до O(n).
### 4. Сравнение операции delete
При случайных данных BST удаляет за **~0.0001 с** (log n). При сортированных — **~0.046 с** (деградация до линейного). HashTable стабильна: ~0.00012 с в обоих случаях.
---
## Выводы и рекомендации
### Когда какую структуру использовать?
| Сценарий | Рекомендация |
|---|---|
| **Частый поиск** по имени | HashTable или BST (случайные данные) |
| **Данные приходят отсортированными** | HashTable (BST деградирует!) |
| **Нужен отсортированный список** | BST (in-order обход — бесплатный) |
| **Частые вставки/удаления + поиск** | HashTable |
| **Минимальная память, простота** | LinkedList (для малых N) |
| **Диапазонные запросы** (все имена AM) | BST |
### Сложности операций
| Структура | Insert | Find | Delete | List (sorted) |
|---|---|---|---|---|
| LinkedList | O(n) | O(n) | O(n) | O(n log n) |
| HashTable | O(1) avg | O(1) avg | O(1) avg | O(n log n) |
| BST (сбалансированный) | O(log n) | O(log n) | O(log n) | O(n) |
| BST (вырожденный) | O(n) | O(n) | O(n) | O(n) |
### Главный вывод
HashTable — лучший выбор для телефонного справочника при частых вставках и поисках. BST лучше HashTable только если нужен отсортированный вывод без дополнительной сортировки — но при условии случайного порядка вставки или использования самобалансирующегося дерева (AVL, Red-Black).