# Отчёт по лабораторной работе
## Тема: Сравнение производительности структур данных для телефонного справочника

---

## 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` — график сравнения производительности

