Я немного читал о деревьях двоичного поиска и о том, как они были разработаны для быстрого поиска, и это заставило меня задуматься. Как бы выглядел поиск из простого хеш-набора по сравнению с одним из этих деревьев.
Давайте определимся с этим вопросом немного лучше:
Учитывая структуру данных ‹int› длины n - сколько времени нужно, чтобы найти известное значение в этой структуре и сделать вывод, что значение не существует в этой структуре.
Рассмотрим массив длиной 5:
[1,2,3,4,5] - Сколько времени потребуется, чтобы найти значение 4 в этом массиве и выяснить, что 6 нет в массиве.
Старое доброе вбрасывание было единственным способом выяснить это *
* Помимо Google, книг, Интернета, коллег и всех, кто умеет программировать.
Претенденты:
· Скромный массив в качестве контрольной группы.
· Hashset ‹int›. Поскольку в этом эксперименте мы используем только целые числа, мы будем использовать HashSet (словарь, в котором ключ == Hashed (значение))
· Дерево двоичного поиска - не является встроенным типом данных в .Net, который относительно легко создать и известен быстрым поиском.
Дерево поиска:
BST - это не структура данных, которую мы используем в повседневном программировании, поэтому вот краткий обзор того, как я создал свою.
BST - это набор узлов (см. Ниже). Каждый узел имеет значение и не более двух других узлов - левого и правого. Каждый дочерний узел, который меньше родительского, хранится слева, а каждый дочерний узел, который больше родительского узла, сохраняется справа.
Преимущества использования BST заключаются в том, что вам не нужно перебирать всю структуру данных, чтобы найти значение или выполнять какой-то алгоритм сортировки.
Создать дерево относительно просто с помощью рекурсивной функции:
Что называется так:
Здесь стоит упомянуть, что я знаю, насколько этот код неэффективен. В любом случае мы в конечном итоге перечисляем весь массив, и рекурсия также будет занимать память, но помните, мы здесь, чтобы проверить скорость поиска, поэтому мы пока не будем это рассматривать.
Все, что мы делаем, чтобы найти значение в BST:
Итак, чтобы провести эксперимент, я создал образец набора данных:
Значение в середине массива и значение в конце массива - это значения, которые мы будем искать вместе с одной несуществующей переменной (1000001).
Чтобы сделать это справедливым, я использовал шкалу Log10 (n), начиная с 200k int в массиве.
Результаты:
Поэтому мне пришлось записывать результаты в тиках, потому что, как бы вы ни старались, выполнение этой операции на 8-ядерном процессоре с тактовой частотой 4 ГГц никогда не будет медленным.
Результаты показали… ничью. Какое разочарование.
Как и следовало ожидать, время поиска в массиве масштабируется с размером набора данных в O (n) режиме. Однако и алгоритм двоичного дерева поиска, и метод Hashset.Contains (), похоже, занимали одинаковое количество времени.
Изначально меня удивила производительность алгоритма BST. Но когда вы думаете об этом, в конце концов, это не так уж и удивительно.
Метод FindValue на узле будет вызываться с максимальным значением Log2 (n), где n - начальный размер выборки (поскольку это максимальная глубина BST).
См. Таблицу ниже:
Для увеличения количества элементов в выборке в 10 раз требуется всего лишь 3–4 дополнительных вызова методов, что означает практически полное отсутствие времени. Отличный результат.
Это означает, что он так же эффективен, как и поиск хеш-набора, который печально известен быстро!
Прежде чем мы объявим победу двоичного дерева поиска, эта реализация (заведомо далекая от идеала) содержала почти в 10 раз больше памяти, чем хэш-набор, и требовала почти в 10 раз больше времени для создания. Я подозревал, что столько же кажущихся, хотя бесчисленные часы исследований были потрачены на уточнение и усовершенствование хеш-набора, и около 45 минут ушло на создание моего двоичного дерева поиска.
Все еще:
Хешсет 1–0 BST