дерево Ван Эмде Боаса | |
---|---|
Тип | Небинарное дерево |
Изобретено | 1975 |
Изобрел | Питер ван Эмде Боас |
Асимптотическая сложность. в нотации большого O | |
Пробел | O (M) |
Поиск | O (журнал журнал M) |
Вставить | O (журнал журнал M) |
Удалить | O (журнал журнал M) |
A дерево ван Эмде Боаса (голландское произношение: ), также известное как дерево vEB или очередь приоритетов ван Эмде Боаса, представляет собой древовидную структуру данных , которая реализует ассоциативный массив с m-битными целочисленными ключами. Он выполняет все операции за время O (log m) или, что эквивалентно, за время O (log log M), где M = 2 - максимальное количество элементов, которые могут быть сохранены в дереве. M не следует путать с фактическим количеством элементов, хранящихся в дереве, по которому часто измеряется производительность других древовидных структур данных. Дерево vEB имеет хорошую эффективность использования пространства, когда оно содержит много элементов, как обсуждается ниже. Он был изобретен группой под руководством голландского компьютерного ученого Питера ван Эмде Боаса в 1975 году.
VEB поддерживает операции с упорядоченным ассоциативным массивом , который включает обычные операции с ассоциативным массивом, а также еще две операции с порядком, FindNext и FindPrevious:
Дерево vEB также поддерживает операции Minimum и Maximum, которые возвращают минимум и максимум элемент хранится в дереве соответственно. Оба они выполняются за время O (1), поскольку минимальный и максимальный элемент хранятся как атрибуты в каждом дереве.
Для простоты, пусть log 2 m = k для некоторого целого k. Определите M = 2. В vEB-дереве T над вселенной {0,..., M − 1} есть корневой узел, в котором хранится массив T.children длины √M. T.children [i] - указатель на дерево vEB, которое отвечает за значения {i√M,..., (i + 1) √M − 1}. Кроме того, T хранит два значения T.min и T.max, а также вспомогательное дерево vEB T.aux.
Данные хранятся в дереве vEB следующим образом: наименьшее значение в настоящее время в дереве сохраняется в T.min, а наибольшее значение сохраняется в T.max. Обратите внимание, что T.min не хранится где-либо еще в дереве vEB, а T.max хранится. Если T пусто, мы используем соглашение, согласно которому T.max = −1 и T.min = M. Любое другое значение x сохраняется в поддереве T.children [i], где i = ⌊x / √M⌋. Вспомогательное дерево T.aux отслеживает, какие дочерние элементы непусты, поэтому T.aux содержит значение j тогда и только тогда, когда T.children [j] непусто.
Операция FindNext (T, x), которая ищет преемника элемента x в дереве vEB, выполняется следующим образом: Если x Обратите внимание, что в любом случае алгоритм выполняет O (1) работу, а затем, возможно, рекурсивно обрабатывает поддерево над юниверсом размера M (юниверсом m / 2 бит). Это дает повторение времени выполнения , который преобразуется в O (log m) = O (log log M). Вызов insert (T, x), который вставляет значение x в дерево vEB T, работает следующим образом: В коде: Ключ к эффективности этой процедуры заключается в том, что вставка элемента в пустое дерево vEB занимает время O (1). Таким образом, даже если алгоритм иногда делает два рекурсивных вызова, это происходит только тогда, когда первый рекурсивный вызов был в пустом поддереве. Это дает ту же повторяемость времени работы по-прежнему. Удаление из деревьев vEB - самая сложная из операций. Вызов Delete (T, x), который удаляет значение x из vEB-дерева T, работает следующим образом: В коде: Опять же, эффективность этой процедуры зависит от того, что удаление из vEB дерево, содержащее только один элемент, занимает только постоянное время. В частности, последняя строка кода выполняется, только если x был единственным элементом в T.children [i] до удаления. . Предположение, что журнал m - целое число, необязательно. Операции и можно заменить, взяв только m / 2⌉ битов высшего порядка и ⌊m / 2⌋ битов младшего порядка соответственно. На любой существующей машине это более эффективно, чем вычисления деления или остатка. Описанная выше реализация использует указатели и занимает общее пространство O (M) = O (2). Это можно увидеть следующим образом. Повторяемость: . Решение, которое привело бы к . К счастью, можно также по индукции показать, что S (M) = M − 2. В практических реализациях, особенно на машинах со сдвигом на k и командами поиска первого нуля, производительность может быть дополнительно улучшена за счет переключение на битовый массив при достижении m, равного размеру слова (или небольшому кратному ему). Поскольку все операции над одним словом являются постоянным временем, это не влияет на асимптотическую производительность, но позволяет избежать большей части хранилища указателей и нескольких разыменований указателей, достигая значительной практической экономии времени и пространства с помощью этого трюка. Очевидная оптимизация деревьев vEB - отбрасывать пустые поддеревья. Это делает деревья vEB довольно компактными, когда они содержат много элементов, поскольку поддеревья не создаются до тех пор, пока к ним что-то не нужно добавить. Первоначально каждый добавленный элемент создает около log (m) новых деревьев, содержащих около m / 2 указателей вместе. По мере роста дерева все больше и больше поддеревьев используется повторно, особенно более крупные. В полном дереве из 2 элементов используется только пространство O (2). Более того, в отличие от двоичного дерева поиска, большая часть этого пространства используется для хранения данных: даже для миллиардов элементов указатели в полном дереве vEB исчисляются тысячами. Однако для небольших деревьев накладные расходы, связанные с деревьями vEB, огромны: порядка . Это одна из причин, по которой они не пользуются популярностью на практике. Один из способов преодоления этого ограничения - использовать только фиксированное количество бит на уровень, что приводит к trie. В качестве альтернативы, каждая таблица может быть заменена хэш-таблицей , уменьшая пространство до O (n log M) (где n - количество элементов, хранящихся в структуре данных) за счет создания структуры данных рандомизированный. Были предложены другие структуры, включая попыток y-fast и попыток x-fast, которые имеют сопоставимое время обновления и запроса, а также используют рандомизированные хэш-таблицы для уменьшения пространства до O (n) или O (n log M).функция FindNext (T, x) ifx < T.min затемвозврат T.min если x ≥ T.max, то / / нет следующего элемента return M i = floor (x / √M) lo = x mod √M если lo < T.children[i].max тогдаreturn ( √M i) + FindNext (T.children [i], lo) j = FindNext (T.aux, i) return (√M j) + T.children [j].min end
Insert
function Insert (T, x) if T.min>T.max then // T пусто T.min = T.max = x; return ifx < T.min, затем swap (x, T.min) если x>T.max, то T.max = xi = floor (x / √M) lo = x mod √M Insert (T.children [i], lo) если T.children [i].min == T.children [i].max тогда Insert (T.aux, i) end
Удалить
function Delete (T, x) если T.min == T.max == x, то T.min = M T.max = −1 return ifx = = T.min, затем hi = T.aux.min * √M j = T.aux.min T.min = x = hi + T.children [j].min i = floor (x / √M) lo = x mod √M Удалить (T.children [i], lo) если T.children [i] пуст, то Удалить (T.aux, i) если x == T.max, то ifT.aux пуст, тогда T.max = T.min иначе hi = T. aux.max * √M j = T.aux.max T.max = hi + T.children [j].max end
Реализация Python
из math import ceil, log2 "" "van Emde Boas Tree - это структура данных, которая дает время запроса O (log (log (u)) для таких операций, как вставка, поиск, удаление, класс преемника и предшественника VEB содержит атрибут min, max, u, w, кластер и сводку изначально min = max = NULL u = размер юниверса (диапазон общих возможных записей) w = длина слова (количество бит в u) w = log2 (u) кластер - это массив структур VEB размером sqrt (u); сводка - это VEB размера sqrt (u), когда размер структуры VEB достигает, мы не сохраняем кластеры, а суммарный вектор min и max достаточно, чтобы сохранить эту структуру. "" "class VEB:" "" Индекс x может быть определен как номер кластера, а позиция внутри кластера, например, позволяет рассмотреть 11 в двоичном формате, он записывается как 1011, поэтому первая половина двоичного strinig дает номер кластера и 2-я половина дает поститон внутри кластера номер кластера = int (10) = 2 позиция внутри кластера = int (11) = 3, поэтому 11 находится во 2-м кластере на 3-м p позиция, где счет начинается с 0-й позиции 0,1,2,3 | 4,5,6,7 | 8,9,10,11 | 12,13,14,15 ^ здесь мы используем 'c' для обозначения номера кластера и 'i' для обозначения индекса внутри кластера, поэтому x можно представить как
Обсуждение
Дополнительная литература