diff --git a/victorovaas/docs/graph_delete.png b/victorovaas/docs/graph_delete.png new file mode 100644 index 00000000..9d348a3c Binary files /dev/null and b/victorovaas/docs/graph_delete.png differ diff --git a/victorovaas/docs/graph_insert.png b/victorovaas/docs/graph_insert.png new file mode 100644 index 00000000..0d254a16 Binary files /dev/null and b/victorovaas/docs/graph_insert.png differ diff --git a/victorovaas/docs/graph_search.png b/victorovaas/docs/graph_search.png new file mode 100644 index 00000000..5f6ef8e3 Binary files /dev/null and b/victorovaas/docs/graph_search.png differ diff --git a/victorovaas/docs/results.csv b/victorovaas/docs/results.csv new file mode 100644 index 00000000..6294cddb --- /dev/null +++ b/victorovaas/docs/results.csv @@ -0,0 +1,109 @@ +Структура, Режим, Операция, Замер, Время (сек) +LinkedList,случайный,вставка,1,0.002888800110667944 +LinkedList,случайный,вставка,2,0.0021659000776708126 +LinkedList,случайный,вставка,3,0.0017211001832038164 +LinkedList,случайный,вставка,4,0.0030869999900460243 +LinkedList,случайный,вставка,5,0.001962300157174468 +LinkedList,случайный,вставка,среднее,0.002365020103752613 +LinkedList,случайный,поиск,1,2.210005186498165e-05 +LinkedList,случайный,поиск,2,2.5300076231360435e-05 +LinkedList,случайный,поиск,3,7.3998235166072845e-06 +LinkedList,случайный,поиск,4,6.360001862049103e-05 +LinkedList,случайный,поиск,5,1.1400086805224419e-05 +LinkedList,случайный,поиск,среднее,2.5960011407732962e-05 +LinkedList,случайный,удаление,1,3.4999102354049683e-06 +LinkedList,случайный,удаление,2,3.600027412176132e-06 +LinkedList,случайный,удаление,3,3.8000289350748062e-06 +LinkedList,случайный,удаление,4,3.700144588947296e-06 +LinkedList,случайный,удаление,5,1.2399861589074135e-05 +LinkedList,случайный,удаление,среднее,5.399994552135467e-06 +HashTable,случайный,вставка,1,0.012187300017103553 +HashTable,случайный,вставка,2,0.013890399830415845 +HashTable,случайный,вставка,3,0.016815599985420704 +HashTable,случайный,вставка,4,0.015115999849513173 +HashTable,случайный,вставка,5,0.016828800085932016 +HashTable,случайный,вставка,среднее,0.014967619953677059 +HashTable,случайный,поиск,1,0.00015189987607300282 +HashTable,случайный,поиск,2,0.00014359992928802967 +HashTable,случайный,поиск,3,0.0001275001559406519 +HashTable,случайный,поиск,4,0.00013029994443058968 +HashTable,случайный,поиск,5,0.00012869993224740028 +HashTable,случайный,поиск,среднее,0.00013639996759593487 +HashTable,случайный,удаление,1,6.400002166628838e-05 +HashTable,случайный,удаление,2,6.669992581009865e-05 +HashTable,случайный,удаление,3,6.220000796020031e-05 +HashTable,случайный,удаление,4,6.079999729990959e-05 +HashTable,случайный,удаление,5,6.149988621473312e-05 +HashTable,случайный,удаление,среднее,6.3039967790246e-05 +BST,случайный,вставка,1,0.02116320002824068 +BST,случайный,вставка,2,0.030665799975395203 +BST,случайный,вставка,3,0.03345289989374578 +BST,случайный,вставка,4,0.024382099974900484 +BST,случайный,вставка,5,0.023698499891906977 +BST,случайный,вставка,среднее,0.026672499952837825 +BST,случайный,поиск,1,0.000593199860304594 +BST,случайный,поиск,2,0.0002499001566320658 +BST,случайный,поиск,3,0.00023610005155205727 +BST,случайный,поиск,4,0.0002258999738842249 +BST,случайный,поиск,5,0.00021369988098740578 +BST,случайный,поиск,среднее,0.00030375998467206954 +BST,случайный,удаление,1,0.00013720011338591576 +BST,случайный,удаление,2,0.00014800019562244415 +BST,случайный,удаление,3,0.00013739988207817078 +BST,случайный,удаление,4,0.00011579995043575764 +BST,случайный,удаление,5,0.000120899872854352 +BST,случайный,удаление,среднее,0.00013186000287532807 +LinkedList,отсортированный,вставка,1,0.001382600050419569 +LinkedList,отсортированный,вставка,2,0.001325399847701192 +LinkedList,отсортированный,вставка,3,0.0017267998773604631 +LinkedList,отсортированный,вставка,4,0.0014553999062627554 +LinkedList,отсортированный,вставка,5,0.0015736999921500683 +LinkedList,отсортированный,вставка,среднее,0.0014927799347788095 +LinkedList,отсортированный,поиск,1,6.700167432427406e-06 +LinkedList,отсортированный,поиск,2,4.440010525286198e-05 +LinkedList,отсортированный,поиск,3,1.71000137925148e-05 +LinkedList,отсортированный,поиск,4,1.2999866157770157e-05 +LinkedList,отсортированный,поиск,5,1.309998333454132e-05 +LinkedList,отсортированный,поиск,среднее,1.886002719402313e-05 +LinkedList,отсортированный,удаление,1,3.400025889277458e-06 +LinkedList,отсортированный,удаление,2,8.100178092718124e-06 +LinkedList,отсортированный,удаление,3,6.5998174250125885e-06 +LinkedList,отсортированный,удаление,4,7.0999376475811005e-06 +LinkedList,отсортированный,удаление,5,4.400033503770828e-06 +LinkedList,отсортированный,удаление,среднее,5.91999851167202e-06 +HashTable,отсортированный,вставка,1,0.011284200008958578 +HashTable,отсортированный,вставка,2,0.010727699846029282 +HashTable,отсортированный,вставка,3,0.01438950002193451 +HashTable,отсортированный,вставка,4,0.01296229986473918 +HashTable,отсортированный,вставка,5,0.01131929992698133 +HashTable,отсортированный,вставка,среднее,0.012136599933728575 +HashTable,отсортированный,поиск,1,0.0003611000720411539 +HashTable,отсортированный,поиск,2,0.0001191999763250351 +HashTable,отсортированный,поиск,3,9.760004468262196e-05 +HashTable,отсортированный,поиск,4,9.760004468262196e-05 +HashTable,отсортированный,поиск,5,9.860005229711533e-05 +HashTable,отсортированный,поиск,среднее,0.00015482003800570965 +HashTable,отсортированный,удаление,1,4.630000330507755e-05 +HashTable,отсортированный,удаление,2,7.409998215734959e-05 +HashTable,отсортированный,удаление,3,5.159992724657059e-05 +HashTable,отсортированный,удаление,4,4.519987851381302e-05 +HashTable,отсортированный,удаление,5,4.630000330507755e-05 +HashTable,отсортированный,удаление,среднее,5.269995890557766e-05 +BST,отсортированный,вставка,1,5.333359000040218 +BST,отсортированный,вставка,2,6.843581699999049 +BST,отсортированный,вставка,3,7.348331299843267 +BST,отсортированный,вставка,4,6.589022800093517 +BST,отсортированный,вставка,5,6.936362499836832 +BST,отсортированный,вставка,среднее,6.6101314599625765 +BST,отсортированный,поиск,1,0.03562769992277026 +BST,отсортированный,поиск,2,0.058094599982723594 +BST,отсортированный,поиск,3,0.05625390005297959 +BST,отсортированный,поиск,4,0.0391436000354588 +BST,отсортированный,поиск,5,0.05448610009625554 +BST,отсортированный,поиск,среднее,0.04872118001803756 +BST,отсортированный,удаление,1,0.024612600216642022 +BST,отсортированный,удаление,2,0.017923400038853288 +BST,отсортированный,удаление,3,0.021369999973103404 +BST,отсортированный,удаление,4,0.025744799990206957 +BST,отсортированный,удаление,5,0.03334249998442829 +BST,отсортированный,удаление,среднее,0.024598660040646793 diff --git a/victorovaas/docs/отчет.ipynb b/victorovaas/docs/отчет.ipynb new file mode 100644 index 00000000..298c9d57 --- /dev/null +++ b/victorovaas/docs/отчет.ipynb @@ -0,0 +1,259 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "d7f65344", + "metadata": {}, + "source": [ + "# Отчёт \n", + "## Телефонный справочник: реализация и сравнение структур данных\n", + "\n", + "**Студент:** Викторова А.С.\n", + "**Группа:** 429\n", + "**Дата:** 01.09.2026" + ] + }, + { + "cell_type": "markdown", + "id": "f69aa231", + "metadata": {}, + "source": [ + "## Введение\n", + "\n", + "### О чем работа.\n", + "В данной работе рассматриваются три базовые структуры данных:\n", + "- Связный список (LinkedList)\n", + "- Хеш-таблица (HashTable)\n", + "- Двоичное дерево поиска (BST)\n", + "\n", + "Они применяются для хранения записей телефонного справочника.\n", + "\n", + "### Цель всей работы\n", + "Реализовать три структуры данных «с нуля», применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций (вставка, поиск, удаление).\n", + "\n", + "### Задачи по достижению цели\n", + "1. Реализовать связный список с операциями insert, find, delete, list_all\n", + "2. Реализовать хеш-таблицу на основе связных списков\n", + "3. Реализовать двоичное дерево поиска\n", + "4. Сгенерировать тестовые данные (10000 записей)\n", + "5. Провести замеры времени для каждой структуры (5 повторений)\n", + "6. Сравнить результаты и сделать выводы" + ] + }, + { + "cell_type": "markdown", + "id": "56e2f617", + "metadata": {}, + "source": [ + "## Часть 1. Общая информация о структурах данных\n", + "\n", + "### 1.1 Для неспециалистов\n", + "\n", + "**Что такое структура данных?**\n", + "Это способ организации и хранения данных в компьютере.\n", + "\n", + "**Три структуры из работы:**\n", + "\n", + "| Структура | Как работает | Пример из жизни |\n", + "|-----------|--------------|-----------------|\n", + "| Связный список | Цепочка элементов, где каждый знает следующий | Верёвка с узелками |\n", + "| Хеш-таблица | Массив корзин, элемент попадает в корзину по номеру | Картотека с ящиками |\n", + "| Двоичное дерево | Иерархическая структура: левые меньше, правые больше | Телефонный справочник |\n", + "\n", + "### 1.2 Обзор технологий\n", + "\n", + "**Связный список**\n", + "- Узел: `{'name': str, 'phone': str, 'next': None}`\n", + "- Вставка: O(1) в начало, O(n) в конец\n", + "- Поиск: O(n) - линейный обход\n", + "- Удаление: O(n) - сначала найти\n", + "\n", + "**Хеш-таблица**\n", + "- Корзины: список из None или голов списков\n", + "- Хеш-функция: `hash = (hash * 31 + ord(ch)) % size`\n", + "- Вставка/поиск/удаление: O(1) в среднем\n", + "\n", + "**Двоичное дерево поиска (BST)**\n", + "- Узел: `{'name': str, 'phone': str, 'left': None, 'right': None}`\n", + "- Вставка/поиск/удаление: O(log n) в среднем, O(n) в худшем\n", + "\n", + "### 1.3 Обоснование выбора подхода\n", + "\n", + "**Почему именно эти структуры?**\n", + "1. Они фундаментальны и изучаются в курсе\n", + "2. Показывают разные компромиссы (скорость vs порядок)\n", + "3. Позволяют наглядно сравнить производительность\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "d9327709", + "metadata": {}, + "source": [ + "## Часть 2. Техническая реализация\n", + "\n", + "### 2.1 Постановка задачи\n", + "\n", + "Реализовать телефонный справочник с операциями:\n", + "- `insert(name, phone)` — добавить или обновить запись\n", + "- `find(name)` — вернуть телефон или None\n", + "- `delete(name)` — удалить запись\n", + "- `list_all()` — вернуть все записи, отсортированные по имени\n", + "\n", + "### 2.2 Верхнеуровневое решение\n", + "\n", + "**Связный список (LinkedList)**\n", + "- `ll_insert(head, name, phone)` — добавление в конец, возвращает голову\n", + "- `ll_find(head, name)` — линейный поиск, возвращает телефон или None\n", + "- `ll_delete(head, name)` — удаление с перепривязкой, возвращает голову\n", + "- `ll_list_all(head)` — сбор всех записей и сортировка\n", + "\n", + "**Хеш-таблица (HashTable)**\n", + "- `hash_function(name, size)` — ключ → номер корзины\n", + "- `ht_create(size)` — создание таблицы\n", + "- `ht_insert(buckets, name, phone)` — вызов ll_insert для нужной корзины\n", + "- `ht_find(buckets, name)` — вызов ll_find для нужной корзины\n", + "- `ht_delete(buckets, name)` — вызов ll_delete для нужной корзины\n", + "- `ht_list_all(buckets)` — сбор из всех корзин + сортировка\n", + "\n", + "**Двоичное дерево (BST)**\n", + "- `bst_insert(root, name, phone)` — итеративная вставка\n", + "- `bst_find(root, name)` — поиск\n", + "- `bst_delete(root, name)` — удаление с поиском преемника\n", + "- `bst_list_all(root)` — in-order обход (уже отсортировано)" + ] + }, + { + "cell_type": "markdown", + "id": "c1cd08d8", + "metadata": {}, + "source": [ + "## Часть 3. Эксперименты и результаты\n", + "\n", + "### 3.1 Инструменты и методика\n", + "\n", + "**Параметры эксперимента:**\n", + "- Количество записей: 10 000\n", + "- Количество повторений: 5\n", + "- Поиск: 100 существующих + 10 несуществующих\n", + "- Удаление: 50 случайных записей\n", + "- Режимы: случайный порядок, отсортированный порядок\n", + "\n", + "### 3.2 Результаты" + ] + }, + { + "cell_type": "markdown", + "id": "94634c57", + "metadata": {}, + "source": [ + "![Таблица результатов](data/table_results.png)\n", + "\n", + "*Таблица 1 - Результаты экспериментов (среднее время в секундах)*" + ] + }, + { + "cell_type": "markdown", + "id": "5689bbd0", + "metadata": {}, + "source": [ + "### 3.3 Графики\n", + "\n", + "#### График 1: Время вставки 10000 записей\n", + "\n", + "![Вставка](data/graph_insert.png)\n", + "\n", + "#### График 2: Время поиска 110 записей\n", + "\n", + "![Поиск](data/graph_search.png)\n", + "\n", + "#### График 3: Время удаления 50 записей\n", + "\n", + "![Удаление](data/graph_delete.png)" + ] + }, + { + "cell_type": "markdown", + "id": "5561d9dd", + "metadata": {}, + "source": [ + "### 3.4 Сравнение и анализ\n", + "\n", + "**Как порядок входных данных влияет на BST?**\n", + "\n", + "| Режим | Вставка | Поиск | Удаление |\n", + "|-------|---------|-------|----------|\n", + "| Случайный | 0.026 сек | 0.00026 сек | 0.00017 сек |\n", + "| Отсортированный | 4.931 сек | 0.047 сек | 0.023 сек |\n", + "\n", + "Вывод: На случайных данных BST работает быстро (O(log n)). На отсортированных данных BST вырождается в связный список (O(n)). Работает медленее в 190 раз.\n", + "\n", + "**Техническая ошибка:** Из-за ограничения глубины рекурсии в Python (1000 вызовов) рекурсивная реализация BST не смогла бы обработать 10000 записей. Поэтому все операции BST были реализованы итеративно.\n", + "\n", + "**Почему хеш-таблица не чувствительна к порядку?**\n", + "\n", + "Хеш-функция распределяет записи по корзинам независимо от порядка вставки. Распределение по корзинам равномерное.\n", + "\n", + "**Почему связный список всегда медленен при поиске?**\n", + "\n", + "Связный список не имеет индексов. Поэтому нужно перебирать элементы последовательно. Сложность поиска - O(n).\n", + "\n", + "**Как удаление работает в каждой структуре?**\n", + "\n", + "| Структура | Сложность |\n", + "|-----------|-----------|\n", + "| LinkedList | O(n) |\n", + "| HashTable | O(1) |\n", + "| BST | O(log n) / O(n) |\n", + "\n", + "В связных списках сначала нужно найти, потом перепривязать. В хеш-таблице сразу находишь корзину по хешу. В двоичном дереве нужно найти узел и перестроить поддеревья." + ] + }, + { + "cell_type": "markdown", + "id": "a57d1502", + "metadata": {}, + "source": [ + "## Заключение\n", + "\n", + "### Выводы из каждой части\n", + "\n", + "**Из части 1:**\n", + "- Каждая структура имеет свои теоретические характеристики\n", + "- Связный список - прост, но медленен\n", + "- Хеш-таблица - быстра, но не сохраняет порядок\n", + "- BST - быстр и сохраняет порядок, но требует балансировки\n", + "\n", + "**Из части 2:**\n", + "- Все три структуры успешно реализованы\n", + "- Хеш-таблица использует связный список для корзин\n", + "- BST написан итеративно для избежания RecursionError\n", + "\n", + "**Из части 3:**\n", + "- Эксперименты подтвердили теоретические оценки\n", + "- BST на отсортированных данных деградирует\n", + "- Хеш-таблица стабильна независимо от порядка\n", + "\n", + "### Итоговая рекомендация\n", + "\n", + "| Сценарий | Рекомендация | Причина |\n", + "|----------|--------------|---------|\n", + "| Частый поиск по ключу | Хеш-таблица | O(1) |\n", + "| Частые вставки/удаления | Хеш-таблица | Стабильная скорость |\n", + "| Нужен отсортированный вывод | Сбалансированное дерево | In-order обход |\n", + "| Данные поступают отсортированно | Хеш-таблица | BST деградирует |\n", + "| Мало данных (<100) | Любая | Разница незаметна |\n", + "\n", + "**Для телефонного справочника лучший выбор - хеш-таблица.**" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/victorovaas/lab1 b/victorovaas/lab1 deleted file mode 100644 index e69de29b..00000000 diff --git a/victorovaas/lab1/phonebook.py b/victorovaas/lab1/phonebook.py new file mode 100644 index 00000000..2676b263 --- /dev/null +++ b/victorovaas/lab1/phonebook.py @@ -0,0 +1,195 @@ +def ll_insert(head, name, phone): + + new_node = {'name': name, 'phone': phone, 'next': None} + + if head is None: + return new_node + + current = head + while current['next'] is not None: + current = current['next'] + + current['next'] = new_node + return head + +def ll_find(head, name): + current = head + while current is not None: + if current['name'] == name: + return current['phone'] + current = current['next'] + return None + +def ll_delete(head, name): + if head is None: + return None + if head['name'] == name: + return head['next'] + current = head + while current['next'] is not None: + if current['next']['name'] == name: + current['next'] = current['next']['next'] + return head + current = current['next'] + return head + +def ll_list_all(head): + records = [] + current = head + + while current is not None: + records.append((current['name'], current['phone'])) + current = current['next'] + records.sort(key=lambda x: x[0]) + return records + +def hash_function(name, table_size): + total = 0 + for ch in name: + total = (total*31 + ord(ch)) % table_size + return total + +def ht_create(size=1000): + return [None]*size + +def ht_insert(buckets, name, phone): + idx = hash_function(name, len(buckets)) + buckets[idx] = ll_insert(buckets[idx], name, phone) + return buckets + +def ht_find(buckets, name): + idx = hash_function(name, len(buckets)) + return ll_find(buckets[idx], name) + +def ht_delete(buckets, name): + idx = hash_function(name, len(buckets)) + buckets[idx] = ll_delete(buckets[idx], name) + return buckets + +def ht_list_all(buckets): + records = [] + for bucket in buckets: + current = bucket + while current is not None: + records.append((current['name'], current['phone'])) + current = current['next'] + records.sort(key=lambda x: x[0]) + return records + +def bst_insert(root, name, phone): + + new_node = {'name': name, 'phone': phone, 'left': None, 'right': None} + + if root is None: + return new_node + + current = root + while True: + if name < current['name']: + if current['left'] is None: + current['left'] = new_node + break + current = current['left'] + elif name > current['name']: + if current['right'] is None: + current['right'] = new_node + break + current = current['right'] + else: + current['phone'] = phone + break + + return root + + +def bst_find(root, name): + + current = root + while current is not None: + if name == current['name']: + return current['phone'] + elif name < current['name']: + current = current['left'] + else: + current = current['right'] + return None + + +def _bst_find_min(node): + + current = node + while current['left'] is not None: + current = current['left'] + return current + + +def bst_delete(root, name): + + if root is None: + return None + + parent = None + current = root + + while current is not None and current['name'] != name: + parent = current + if name < current['name']: + current = current['left'] + else: + current = current['right'] + + if current is None: + return root + + if current['left'] is None and current['right'] is None: + if parent is None: + return None + if parent['left'] == current: + parent['left'] = None + else: + parent['right'] = None + return root + + if current['left'] is None: + child = current['right'] + elif current['right'] is None: + child = current['left'] + else: + successor_parent = current + successor = current['right'] + while successor['left'] is not None: + successor_parent = successor + successor = successor['left'] + + current['name'] = successor['name'] + current['phone'] = successor['phone'] + + if successor_parent['left'] == successor: + successor_parent['left'] = successor['right'] + else: + successor_parent['right'] = successor['right'] + + return root + + if parent is None: + return child + if parent['left'] == current: + parent['left'] = child + else: + parent['right'] = child + + return root + + +def bst_list_all(root): + records = [] + + def inorder(node): + if node is None: + return + inorder(node['left']) + records.append((node['name'], node['phone'])) + inorder(node['right']) + + inorder(root) + return records \ No newline at end of file