## Практические графики ### Информация о тестировании - Общее число записей: 20000 - Каждый замер повторялся: 20 раз - Количество существующих записей для случайного поиска: 1000 - Количество несуществующих записей для поиска: 500 - Количество элементов для удаления: 1000 ![[insert.pdf]] **Тестирование вставки (рис. 1)** ![[search.pdf]] **Тестирование поиска (рис. 2)** ![[delete.pdf]] **Тестирование удаления (рис. 3)** ## Анализ результатов ### Как порядок входных данных влияет на скорость вставки в BST (деградация до O(n) на отсортированных данных)? По определению, при вставке отсортированных данных, структура бинарного дерева поиска вырождается в связный список. Для визуализации этого в тесте выводятся высота и количество элементов в дереве: Для случайных данных вывод выглядит примерно так: ``` Высота дерева: 28, элементов: 8634 ``` Для сортированных данных же: ``` Высота дерева: 8634, элементов: 8634 ``` Заметим, что при случайных данных скорость вставки в бинарное дерево почти лишь немного уступает по скорости хеш-таблице. При сортированных данных из-за рекурсивной реализации вставки бинарное дерево проигрывает связному списку(который имеет линейную сложность вставки) ### Почему хеш-таблица почти не чувствительна к порядку. Хеш-таблица не чувствительна к порядку данных, так как использует для распределения элементов хеш значения данных (сложность операции одинакова для любых однотипных данных) и после производит вставку в связный список(в моей реализации проходит по списку и вставляет данные в конец). Поэтому хеш-таблица ни на одном из этапов не сравнивает данные, следовательно их порядок не влияет на скорость. ### Почему связный список всегда медленен при поиске. Операция поиска в связном списке имеет линейную сложность $O(n)$ не зависимо от порядка данных, что можно видеть на графике (см. рис. 2). Для бинарного дерева поиска эта сложность в лучшем случае $O(\log(N))$, а в худшем $O(N)$. Для хеш-таблицы сложность вставки $O(1)$, с хорошей хеш-функцией и низким заполнением. ### Как удаление работает в каждой структуре. #### Связный список Находим элемент перед удаляем элементом, и заменяем его поле `next` на `next.next`, то есть теперь он указывает на элемент, который идёт после удаляемого элемента ``` Go current := ll.head for current.next != nil { if current.next.data.Name == targetName { current.next = current.next.next return true } current = current.next } ``` #### Бинарное дерево поиска После того, как мы нашли узел, который необходимо удалить, у нас возможны три случая. Случай 1: У удаляемого узла нет правого ребенка. В этом случае мы просто перемещаем левого ребенка (3) на место удаляемого узла(5). В результате дерево будет выглядеть так: ``` Удаляем элемент со значением 5 ДО УДАЛЕНИЯ: ПОСЛЕ УДАЛЕНИЯ: [8] [8] / \ / \ [5] [10] [3] [10] / / \ [3] [1] [4] / \ [1] [4] ``` Случай 2: У удаляемого узла есть только правый ребенок, у которого, в свою очередь нет левого ребенка. В этом случае нам надо переместить правого ребенка(8) удаляемого узла (5) на его место. ``` Удаляем элемент со значением 5 До удаления: После удаления: [10] [10] / \ / \ [5] [12] [8] [12] / \ / \ [1] [8] [1] [9] \ [9] ``` Случай 3: У удаляемого узла есть первый ребенок, у которого есть левый ребенок. В этом случае место удаляемого узла занимает крайний левый ребенок правого ребенка удаляемого узла. Давайте посмотрим, почему это так. Мы знаем о поддереве, начинающемся с удаляемого узла следующее: - Все значения справа от него больше или равны значению самого узла. - Наименьшее значение правого поддерева — крайнее левое. Мы должны поместить на место удаляемого узел со значением, меньшим или равным любому узлу справа от него. Для этого нам необходимо найти наименьшее значение в правом поддереве. Поэтому мы берем крайний левый узел правого поддерева. ``` Удаляем элемент со значением 5 До удаления: После удаления: [10] [10] / \ / \ [5] [12] [7] [12] / \ / \ [1] [9] [1] [9] / / [7] [8] \ [8] ``` #### Хеш-таблица Находим индекс элемента в таблица, далее производим удаление элемента в связном списке, который соответствует этому индексу. # Вывод Мы реализовали и протестировали три различные структуры хранения данных: связный список, бинарное дерево поиска и хеш-таблица. Сравнили скорость операций вставки, удаления и поиска для каждой структуры. Если не важен порядок хранения и извлечения данных, то хеш-таблица лучший выбор для быстрых вставки, удаления и поиска. Если нужно хранить данные с возможностью быстрого отсортированного обхода, то стоит выбрать бинарное дерево поиска. Если нужно хранить данные в порядке поступления(например очередь), то стоит выбрать связный список.