Введение

Структура данных дерева случайного поиска в информатике, треп и рандомизированное двоичное дерево поиска — это две тесно связанные формы двоичных деревьев поиска, поддерживающие динамический набор упорядоченных ключей и позволяющие выполнять двоичный поиск по этим ключам. После любой последовательности операций вставки и удаления ключей, структура дерева представляет собой случайную величину с тем же распределением вероятностей, что и у случайного двоичного дерева; в частности, с высокой вероятностью его высота пропорциональна логарифму количества ключей, благодаря чему каждая операция поиска, вставки или удаления выполняется за логарифмическое время.

Описание

Треп был впервые описан Раймундом Зайделем и Сесилией Р. Арагон в 1989 году; его название является портманто из слов «дерево» и «куча». Это картезианское дерево, в котором каждому ключу присваивается (случайно выбранный) числовой приоритет. Как и в любом двоичном дереве поиска, порядок обхода в глубину (inorder traversal) узлов соответствует отсортированному порядку ключей. Структура дерева определяется требованием, чтобы оно было упорядочено как куча: то есть, числовой приоритет любого нелистового узла должен быть больше или равен приоритету его дочерних узлов. Таким образом, как и в случае с картезианскими деревьями в общем смысле, корневой узел имеет максимальный приоритет, а его левое и правое поддеревья формируются аналогичным образом из подпоследовательностей отсортированного порядка, расположенных слева и справа от этого узла. Эквивалентный способ описания трепа заключается в том, что его можно сформировать путем последовательной вставки узлов с наивысшим приоритетом в двоичное дерево поиска без выполнения какой-либо перебалансировки. Следовательно, если приоритеты являются независимыми случайными числами (из распределения на достаточно большом пространстве возможных приоритетов, чтобы обеспечить крайне малую вероятность совпадения приоритетов у двух узлов), то форма трепа имеет такое же распределение вероятностей, как и форма случайного двоичного дерева поиска – дерева поиска, сформированного путем вставки узлов без перебалансировки в случайном порядке. Поскольку случайные двоичные деревья поиска с высокой вероятностью имеют логарифмическую высоту, то же самое справедливо и для трепов. Это аналогично аргументу, используемому для двоичных деревьев поиска, что быстрая сортировка выполняется за ожидаемое время. Если двоичные деревья поиска являются решениями динамической версии задачи сортировки, то трепы соответствуют динамической быстрой сортировке, где приоритеты определяют выбор опорного элемента. Арагон и Зайдель также предлагают присваивать более высокие приоритеты часто обращаемым узлам, например, с помощью процесса, который при каждом обращении выбирает случайное число и заменяет приоритет узла этим числом, если оно выше предыдущего приоритета. Эта модификация приведет к потере случайной формы дерева; вместо этого часто обращаемые узлы будут с большей вероятностью располагаться ближе к корню дерева, что ускорит их поиск. Наор и Ниссим описывают применение трепов для поддержания сертификатов авторизации в криптосистемах с открытым ключом.

Строительство лестницы

Чтобы построить треп, можно просто последовательно вставить n значений, где вставка каждого занимает время. Следовательно, треп можно построить за время из списка значений.

Рандомизированное двоичное дерево поиска

Рандомизированное двоичное дерево поиска, представленное Мартинесом и Рурой после работы Арагона и Сейдела над трепами, хранит те же узлы с тем же случайным распределением формы дерева, но поддерживает различную информацию внутри узлов дерева для сохранения его рандомизированной структуры. Вместо хранения случайных приоритетов в каждом узле, рандомизированное двоичное дерево поиска хранит небольшое целое число в каждом узле – количество его потомков (включая сам узел); эти числа могут поддерживаться во время операций вращения дерева с добавлением лишь постоянного времени за одно вращение. Когда ключ x необходимо вставить в дерево, содержащее уже n узлов, алгоритм вставки выбирает с вероятностью 1/(n + 1) разместить x в качестве нового корня дерева, а в противном случае рекурсивно вызывает процедуру вставки для вставки x в левое или правое поддерево (в зависимости от того, меньше или больше его ключ, чем ключ корня). Количество потомков используется алгоритмом для вычисления необходимых вероятностей для случайного выбора на каждом шаге. Размещение x в корне поддерева может быть выполнено либо как в трепе – вставкой в лист с последующим вращением вверх, либо с помощью альтернативного алгоритма, описанного Мартинесом и Рурой, который разделяет поддерево на две части для использования в качестве левого и правого потомков нового узла. Процедура удаления в рандомизированном двоичном дереве поиска использует ту же информацию на узел, что и процедура вставки, но, в отличие от процедуры вставки, ей требуется в среднем O(1) случайных решений для объединения двух поддеревьев, спускающихся от левого и правого потомков удаленного узла, в одно дерево. Это происходит потому, что поддеревья, которые необходимо объединить, в среднем находятся на глубине Θ(log n); объединение двух деревьев размером n и m требует в среднем Θ(log(n+m)) случайных выборов. Если левое или правое поддерево узла, который необходимо удалить, пусто, операция объединения тривиальна; в противном случае левый или правый потомок удаленного узла выбирается в качестве нового корня поддерева с вероятностью, пропорциональной количеству его потомков, и объединение выполняется рекурсивно.

Сравнение

Информация, хранящаяся в каждом узле рандомизированного двоичного дерева, проще, чем в трепе (небольшое целое число вместо числа с высокой точностью), но требует больше вызовов генератора случайных чисел (O(log n) вызовов при вставке или удалении вместо одного вызова при вставке), а процедура вставки немного сложнее из-за необходимости обновления информации о потомках каждого узла. Незначительное техническое отличие состоит в том, что в трепе существует небольшая вероятность коллизии (два ключа могут получить одинаковый приоритет), и в обоих случаях будут статистические различия между истинным генератором случайных чисел и генератором псевдослучайных чисел, обычно используемым в цифровых компьютерах. Однако в любом случае различия между теоретической моделью идеально случайного выбора, используемой при разработке алгоритма, и возможностями реальных генераторов случайных чисел пренебрежимо малы. Хотя треп и рандомизированное двоичное дерево поиска имеют одинаковое случайное распределение форм дерева после каждого обновления, история изменений деревьев, выполняемых этими двумя структурами данных в последовательности операций вставки и удаления, может различаться. Например, в трепе, если три числа 1, 2 и 3 вставлены в порядке 1, 3, 2, а затем число 2 удалено, оставшиеся два узла сохранят те же отношения родитель-потомок, которые были до вставки среднего числа. В рандомизированном двоичном дереве поиска дерево после удаления с равной вероятностью может представлять собой одно из двух возможных деревьев на этих двух узлах, независимо от того, как выглядело дерево до вставки среднего числа.

Вставка элемента

Чтобы вставить элемент в позицию pos, мы разделяем массив на два подмассива [0, pos) и [pos, sz], вызывая функцию split и получая два дерева, T1 и T2. Затем мы объединяем T1 с новым узлом, вызывая функцию join. Наконец, мы вызываем функцию join для объединения T1 и T2.

Удалить элемент

Мы находим элемент, подлежащий удалению, и выполняем слияние его дочерних элементов L и R. Затем мы заменяем удаляемый элемент деревом, полученным в результате операции слияния.

Обратная сторона в заданном диапазоне

Чтобы показать, что поддерево данного узла необходимо отразить для каждого узла, мы создадим дополнительное булево поле R и установим его значение в true. Чтобы распространить это изменение, мы поменяем местами дочерние узлы данного узла и установим значение R в true для каждого из них.