# Отчёт: Задание 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) |
| **Диапазонные запросы** (все имена A–M) | 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).
