{ "cells": [ { "cell_type": "markdown", "id": "f058dc2c", "metadata": {}, "source": [ "# Отчёт: Задание 1 — Структуры данных\n", "\n", "## Цель работы\n", "\n", "Разработать три структуры данных «с нуля» в процедурном стиле (без ООП), применить их для хранения записей телефонной книги и провести экспериментальное сравнение производительности ключевых операций.\n", "\n", "**Структуры данных:**\n", "- Связный список (LinkedList)\n", "- Хеш-таблица (HashTable)\n", "- Двоичное дерево поиска (BST)\n", "\n", "---\n", "\n", "## Реализация\n", "\n", "### Основные технические решения\n", "\n", "#### 1. Связный список\n", "\n", "Узел реализован как Python-словарь: `{'name': 'Имя', 'phone': '123', 'next': None}`.\n", "\n", "Новые элементы добавляются **в конец** списка за O(1) (без проверки на дубликаты имени). Поиск и удаление работают за линейное время O(n) из-за отсутствия прямого доступа по индексу.\n", "\n", "#### 2. Хеш-таблица\n", "\n", "Фиксированный массив на **1000 корзин**. Каждая корзина — указатель на связный список (метод цепочек). Хеш-функция: полиномиальная с основанием 31, свёрнутая по модулю размера таблицы. Среднее время операций O(1), при коллизиях — O(k), где k — длина цепочки.\n", "\n", "#### 3. Двоичное дерево поиска (BST)\n", "\n", "Узел: `{'name': 'Имя', 'phone': '123', 'left': None, 'right': None}`. Сравнение ключей — лексикографическое по полю `name`. Вставка и поиск реализованы итеративно. Удаление — с заменой на минимальный узел правого поддерева. Обход в глубину даёт отсортированный список.\n", "\n", "---\n", "\n", "## Экспериментальная часть\n", "\n", "### Условия проведения замеров\n", "\n", "| Параметр | Значение |\n", "|---|---|\n", "| Количество записей (N) | 10 000 |\n", "| Количество замеров на операцию | 5 |\n", "| Поисковых запросов | 110 (100 существующих + 10 отсутствующих) |\n", "| Удалений | 50 |\n", "| Размер хеш-таблицы | 1000 корзин |\n", "\n", "**Два набора данных:**\n", "- `records_shuffled` — случайный порядок записей\n", "- `records_sorted` — упорядоченный по имени (алфавитный порядок)\n", "\n", "---\n", "\n", "## Результаты\n", "\n", "### Среднее время выполнения (секунды)\n", "\n", "| Структура | Режим | Вставка (10000) | Поиск (110) | Удаление (50) |\n", "|-----------|-------|----------------|-------------|---------------|\n", "| LinkedList | случайный | 0.001383 | 0.00000514 | 0.00000234 |\n", "| LinkedList | отсортированный | 0.001167 | 0.00000500 | 0.00000230 |\n", "| HashTable | случайный | 0.010394 | 0.00009380 | 0.00004332 |\n", "| HashTable | отсортированный | 0.009557 | 0.00009298 | 0.00004388 |\n", "| BST | случайный | 0.015142 | 0.00013682 | 0.00007188 |\n", "| **BST** | **отсортированный** | **4.19062** | **0.030306** | **0.016941** |\n", "\n", "### Визуализация\n", "\n", "![Сравнение вставки](docs/data/graph_insert.png)\n", "\n", "![Сравнение поиска](docs/data/graph_search.png)\n", "\n", "![Сравнение удаления](docs/data/graph_delete.png)\n", "\n", "![Таблица результатов](docs/data/table_results.png)\n", "\n", "---\n", "\n", "## Анализ результатов\n", "\n", "### 1. Связный список — сверхбыстрая вставка, но линейный поиск\n", "\n", "- **Вставка** выполняется за **~0.0012 с** на 10000 элементов, так как добавление происходит в конец списка без проверки дубликатов (O(1) на операцию).\n", "- **Поиск** и **удаление** в замерах показали микросекунды, но это следствие малого количества операций (110 поисков, 50 удалений) и того, что искомые записи находятся в начале списка. В худшем случае (поиск отсутствующего элемента) сложность остаётся O(n).\n", "\n", "**Вывод:** связный список эффективен только при очень малых объёмах данных или когда вставка — единственная частая операция.\n", "\n", "### 2. Хеш-таблица — стабильная производительность\n", "\n", "Хеш-таблица демонстрирует **устойчивость к порядку входных данных**:\n", "- Вставка: ~0.010 с (быстрее BST на случайных данных, но медленнее LinkedList)\n", "- Поиск: ~0.000094 с (в 18 раз быстрее BST на случайных)\n", "- Удаление: ~0.000043 с (в 1.6 раза быстрее BST)\n", "\n", "Размер таблицы (1000 корзин) обеспечивает равномерное распределение ключей, поэтому производительность остаётся стабильной.\n", "\n", "### 3. BST катастрофически деградирует на упорядоченных данных\n", "\n", "Самый показательный результат эксперимента:\n", "\n", "| Операция | Случайный порядок | Отсортированный порядок | Ухудшение |\n", "|----------|------------------|------------------------|-----------|\n", "| Вставка | 0.01514 с | **4.19062 с** | **×277** |\n", "| Поиск | 0.0001368 с | **0.03031 с** | **×221** |\n", "| Удаление | 0.0000719 с | **0.01694 с** | **×236** |\n", "\n", "**Причина:** при вставке отсортированных данных дерево вырождается в линейный список — каждый новый элемент больше предыдущего и помещается только в правую ветку. Высота дерева становится O(n) вместо O(log n), что превращает все операции в линейные.\n", "\n", "### 4. Сравнение с теоретическими ожиданиями\n", "\n", "| Структура | Теоретическая вставка | Фактическая (случ./сорт.) | \n", "|-----------|----------------------|---------------------------|\n", "| LinkedList | O(1) (в конец) | 0.0014 с / 0.0012 с |\n", "| HashTable | O(1) в среднем | 0.0104 с / 0.0096 с |\n", "| BST | O(log n) в среднем | 0.0151 с (случ.) |\n", "| BST | O(n) в худшем | 4.19 с (сорт.) |\n", "\n", "---\n", "\n", "## Выводы и практические рекомендации\n", "\n", "### Выбор структуры в зависимости от задачи\n", "\n", "| Сценарий | Рекомендация |\n", "|----------|--------------|\n", "| **Частый поиск по ключу** | HashTable (быстрее всего) |\n", "| **Данные поступают упорядоченно** | HashTable (BST непригоден) |\n", "| **Только вставка и редкий поиск** | LinkedList (самая быстрая вставка) |\n", "| **Требуется отсортированный вывод** | BST (обход даёт порядок за O(n)) |\n", "| **Сбалансированные операции** | HashTable |\n", "| **Диапазонные запросы** (например, А–М) | BST (при условии балансировки) |\n", "\n", "### Теоретическая сложность операций \n", "| Структура | Insert | Find | Delete | Обход (отсорт.) |\n", "|-----------|--------|------|--------|-----------------|\n", "| LinkedList (в конец) | O(1) | O(n) | O(n) | O(n log n) |\n", "| HashTable | O(1) | O(1) | O(1) | O(n log n) |\n", "| BST (сбалансированный) | O(log n) | O(log n) | O(log n) | O(n) |\n", "| BST (вырожденный) | O(n) | O(n) | O(n) | O(n) |\n", "\n", "### Ключевой вывод\n", "\n", "Для телефонного справочника с частыми поисками и обновлениями оптимальный выбор — **хеш-таблица**. Она обеспечивает предсказуемую скорость вне зависимости от порядка данных.\n", "\n", "Обычный **связный список** полезен только как вспомогательная структура (например, для цепочек в хеш-таблице) или при минимальном объёме данных.\n", "\n", "**Двоичное дерево поиска** без самобалансировки опасно использовать с реальными данными, которые часто бывают частично или полностью упорядоченными. В таких случаях необходимо применять AVL или красно-чёрные деревья.\n" ] } ], "metadata": { "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }