From ee0aecd7ca6ecf99db2ad147128d1cdc66ba7001 Mon Sep 17 00:00:00 2001 From: Muihiro Date: Tue, 19 May 2026 21:14:37 +0300 Subject: [PATCH] for 1-st ex --- fomichevks/docs/отчет.txt | 137 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 fomichevks/docs/отчет.txt diff --git a/fomichevks/docs/отчет.txt b/fomichevks/docs/отчет.txt new file mode 100644 index 0000000..04bd828 --- /dev/null +++ b/fomichevks/docs/отчет.txt @@ -0,0 +1,137 @@ +1. Цель работы +Реализовать три базовые структуры данных без использования объектно-ориентированных механизмов, применить их для хранения записей телефонного справочника, экспериментально измерить производительность операций вставки, поиска и удаления, а также проанализировать влияние порядка входных данных на время выполнения. + +Связный список (LinkedListPhoneBook) +Узел: {'name': str, 'phone': str, 'next': dict | None} +Операции: +ll_insert: линейный проход до конца, обновление при совпадении имени, вставка нового узла в хвост. Возвращает голову списка. +ll_find: последовательный перебор до первого совпадения. +ll_delete: поиск предшественника удаляемого узла, переназначение ссылки next. +ll_list_all: сбор записей в список, явная сортировка по имени. + +Хеш-таблица с цепочками (HashTable) +Структура: список из BUCKET_COUNT = 1024 элементов, каждый элемент — голова связного списка. +Хеширование: idx = hash(name) % BUCKET_COUNT +Операции: делегируют соответствующим ll_* функциям для конкретного бакета. + +Узел: {'name': str, 'phone': str, 'left': dict | None, 'right': dict | None} +Операции: +bst_insert: рекурсивное сравнение имён, создание листа при достижении None. +bst_find: рекурсивный спуск влево/вправо в зависимости от результата сравнения. +bst_delete: три случая: 0 потомков, 1 потомок, 2 потомка. При двух потомках используется inorder-преемник (минимальный элемент правого поддерева). +bst_list_all: центрированный (in-order) обход, гарантирующий отсортированный вывод без дополнительной сортировки. + +Влияние порядка входных данных на скорость вставки в BST +Двоичное дерево поиска (BST) поддерживает инвариант: left.name < root.name < right.name. При вставке новых узлов алгоритм рекурсивно спускается по дереву, выбирая левую или правую ветвь в зависимости от результата сравнения. + +Случай 1: Случайный порядок данных +Ключи распределяются по дереву хаотично +Левые и правые поддеревья заполняются примерно равномерно +Высота дерева: h ≈ log₂(N) ≈ 14 для N=10 000 +Сложность вставки одного элемента: O(log N) +Общая сложность вставки всех N элементов: O(N log N) + +Случай 2: Отсортированный порядок данных +Каждый следующий ключ больше всех предыдущих +Алгоритм всегда выбирает правую ветвь +Дерево вырождается в линейную цепочку + +Почему хеш-таблица почти не чувствительна к порядку? + +Функция hash() в Python: +Детерминирована: один и тот же ключ → один и тот же хеш +Равномерно распределяет значения по пространству хешей +Не зависит от порядка вызова: hash("User_00001") всегда одинаков + +Распределение по бакетам +При N=10 000 записей и 1024 бакетах: +Ожидаемая загрузка: α = N / BUCKET_COUNT ≈ 9.77 элементов на бакет +Даже если все ключи отсортированы, их хеши «размазываются» по всему диапазону +Внутри каждого бакета хранится короткий связный список (~10 элементов) + +Почему связный список всегда медленен при поиске? + +Связный список хранит элементы последовательно, без индексации +Для поиска элемента с именем X: +Начать с головы списка +Сравнить curr['name'] == X +Если не совпало → перейти к curr['next'] +Повторять до нахождения или конца списка +Связный список не подходит для задач с частым поиском. Его удел очереди, стеки, или вспомогательная роль внутри других структур. + +Как удаление работает в каждой структуре? +Связный список +def ll_delete(head, name): + if head['name'] == name: + return head['next'] + curr = head + while curr['next']: + if curr['next']['name'] == name: + curr['next'] = curr['next']['next'] + return head + curr = curr['next'] + return head + +Поиск узла (или его предшественника) — O(N) +Переназначение ссылки next — O(1) +Сборка мусора (автоматически в Python) + +Хеш-таблица +def ht_delete(buckets, name): + idx = hash(name) % BUCKET_COUNT + buckets[idx] = ll_delete(buckets[idx], name) + +Вычисление индекса бакета — O(1) +Поиск и удаление в связном списке бакета — O(L), где L ≈ 10 +Итого: O(1) в среднем + +Двоичное дерево поиска +def bst_delete(root, name): + # 1. Поиск узла + if name < root['name']: + root['left'] = bst_delete(root['left'], name) + elif name > root['name']: + root['right'] = bst_delete(root['right'], name) + else: + # 2. Три случая удаления + if root['left'] is None: + return root['right'] # 0 или 1 потомок + elif root['right'] is None: + return root['left'] + else: + # 2 потомка: найти inorder-преемника + successor = _bst_find_min(root['right']) + root['name'] = successor['name'] + root['phone'] = successor['phone'] + root['right'] = bst_delete(root['right'], successor['name']) + return root + +Поиск удаляемого узла — O(h) +Обработка случая: +0 потомков: просто удалить узел +1 потомок: «поднять» потомка на место удаляемого +2 потомка: найти минимум в правом поддереве (inorder-преемник), скопировать его данные, рекурсивно удалить преемника +Возврат обновлённого корня поддерева + +Когда какую структуру использовать? + +| Сценарий | Рекомендация | +|---|---| +| **Частый поиск** по имени | 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).