2026-rff_mp/stepushovgs/data-structures/docs/Отчёт.md

8.5 KiB
Raw Blame History

Практические графики

Информация о тестировании

  • Общее число записей: 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, то есть теперь он указывает на элемент, который идёт после удаляемого элемента

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]

Хеш-таблица

Находим индекс элемента в таблица, далее производим удаление элемента в связном списке, который соответствует этому индексу.

Вывод

Мы реализовали и протестировали три различные структуры хранения данных: связный список, бинарное дерево поиска и хеш-таблица. Сравнили скорость операций вставки, удаления и поиска для каждой структуры. Если не важен порядок хранения и извлечения данных, то хеш-таблица лучший выбор для быстрых вставки, удаления и поиска. Если нужно хранить данные с возможностью быстрого отсортированного обхода, то стоит выбрать бинарное дерево поиска. Если нужно хранить данные в порядке поступления(например очередь), то стоит выбрать связный список.