Дерево ван Эмде Боаса
Определение: |
Дерево ван Эмде Боаса — структура данных, представляющая собой дерево поиска, позволяющее хранить целые неотрицательные числа в интервале | и осуществлять над ними все соответствующие дереву поиска операции.
Проще говоря, данная структура позволяет хранить
-битные числа и производить над ними операции , , , , , , и некоторые другие операции, которые присущи всем деревьям поиска.Особенностью этой структуры является то, что все операции выполняются за
, что асимптотически лучше, чем в большинстве других деревьев поиска, где — количество элементов в дереве.Содержание
Структура
Для удобства работы с деревом будем использовать
, равные степени двойки.Как уже было сказано выше,
-дерево хранит числа в интервале . Тогда 1-дерево хранит информацию, содержатся ли в нем 0 и 1.Построим
-дерево, при . В нем будут хранится:- массив , состоящий из -деревьев
- вспомогательное -дерево, которое назовем
- максимальный и минимальный элемент, хранящийся в этом дереве (если оно не является пустым), причем дополнительно в самом дереве эти элементы хранить не будем.
Пусть у нас есть
-битное число . Разобьем это число таким образом, что — число, соответствующее старшим битам числа , а соответствует младшим битам. Тогда информация, хранится ли в данном дереве число , эквивалентна информации, содержится ли в дереве число .Нетрудно увидеть, что высота подобного дерева
, так как каждый следующий уровень дерева содержит числа, количество битов в которых в 2 раза меньше, чем в предыдущем.Во вспомогательном дереве
будем хранить все такие числа , что дерево не пусто.Операции
empty
Чтобы определить, пусто ли дерево, будем изначально инициализировать поле
числом, которое не лежит в интервале . Назовем это число . Например, это может быть , если мы храним в числа в знаковом целочисленном типе, или , если в беззнаковом. Тогда проверка на пустоту дерева будет заключаться лишь в сравнении поля с этим числом.empty(T) if T.min == none return true; else return false;
min и max
Так как мы храним в дереве минимальное и максимальное значения, то данные операции не требуют ничего, кроме вывода значения поля
или в соответствии с запросом. Время выполнения данных операций соответственно .find
Алгоритм поиска сам напрашивается из выше описанной структуры:
- если дерево пусто, то число не содержится в нашей структуре
- если число равно полю , то число в дереве есть
- иначе ищем число в поддереве
find(T, x) if empty(T) return false; if T.min == x or T.max == x return true; return find(T.children[high(x)], low(x));
insert
Операция вставки элемента
состоит из нескольких частей:- если дерево пусто или в нем содержится единственный элемент ( = ), то присвоим полям и соответствующие значения. Делать что-то еще бессмысленно, так как информация записанная в и полностью описывает состояние текущего дерева и удовлетворяет структуре нашего дерева.
- иначе:
- если элемент больше или меньше текущего дерева, то обновим соответствующее значение минимума или максимума, а старый минимум или максимум добавим в дерево
- вставим во вспомогательное дерево число , если соответствующее поддерево до этого было пусто
- вставим число в поддерево , за исключением ситуации, когда текущее дерево — это 1-дерево, и дальнейшая вставка не требуется
insert(T, x) if empty(T) // проверка на пустоту текущего дерева T.min = x; T.max = x; else if T.min == T.max // проверка, что в дереве один элемент if T.min < x T.max = x; else T.min = x; else if T.min > x swap(T.min, x); // релаксация минимума if T.max < x swap(T.max, x); // релаксация максимума if T.k != 1 if empty(T.children[high(x)]) insert(T.aux, high(x)); // вставка high(x) во вспомогательно дерево aux insert(T.children[high(x)], low(x)); // вставка low(x) в поддерево children[high(x)]
Нетрудно увидеть, что данная операция работает за время
. На каждом уровне дерева мы выполняем операций. После этого возможны 2 случая: поддерево пусто, и мы будем производить дальнейшую вставку и в него, и во вспомогательное дерево , или же поддерево не пусто, и мы просто спустимся на уровень ниже. Но если поддерево пусто, то вставка в него будет выполнена за , так как мы всего лишь обновим поля и . Все остальные операции будут выполнятся уже со вспомогательным деревом , высота которого на 1 уровень меньше, чем высота текущего. Если же поддерево не пусто, то мы просто перейдем к вставке элемента в это поддерево, высота которого так же на 1 меньше, чем у текущего. В итоге, каждый раз, выполнив операций, мы переходим к дереву, высота которого на 1 меньше, чем у текущего. Следовательно, количество операций пропорционально высоте дерева, которая, как уже было показано, . То есть операция вставки займет времени.remove
Удаление из дерева T также делится на несколько подзадач:
- Если T.min = T.max = x, значит в дереве один элемент, мы его удалим и как-нибудь пометим, что дерево пусто(на будущее).
- Если x = T.min,то мы должны найти следующий второй минимум удалить его из того места где он находится и поставить в T.min Второй минимум - это либо T.max, либо T.children[T.aux.min].min.
Аналогично для случая x = T.max
- Если же x = T.min и x = T.max, то мы должны удалить x из поддерева i отвечающего x.
Важно, что Delete реализован рекурсивно от дерева в котором идет удаления. Так же нельзя забывать, что если мы удаляем последнее вхождение x, то мы должны удалить i из вспомогательного дерева.
Delete(T, x) if (T.min == T.max == x) T.min = M T.max = -1 return if (x == T.min) if (T.aux is empty) T.min = T.max return else x = T.children[T.aux.min].min T.min = x if (x == T.max) if (T.aux is empty) T.max = T.min return else x = T.children[T.aux.max].max T.max = x if (T.aux is empty) return i = floor(x/sqrt(M)) Delete(T.children[i], x%sqrt(M)) if (T.children[i] is empty) Delete(T.aux, i) (с)wikipedia.org