Дерево ван Эмде Боаса — различия между версиями
Warrior (обсуждение | вклад) (→insert) |
Warrior (обсуждение | вклад) (→remove) |
||
| Строка 87: | Строка 87: | ||
== remove == | == remove == | ||
| − | Удаление из дерева | + | Удаление из дерева также делится на несколько подзадач: |
| − | *Если | + | *Если <tex> min </tex> = <tex> max </tex> = <tex> x </tex>, значит в дереве один элемент, удалим его и отметим, что дерево пусто |
| − | *Если x = | + | *Если <tex> x = min </tex>, то мы должны найти следующий минимальный элемент в этом дереве, присвоить <tex>min</tex> значение второго минимального элемента и удалить его из того места, где он хранится. Второй минимум {{---}} это либо <tex> max </tex>, либо <tex> children[aux.min].min </tex> (для случая <tex> x = max </tex> действуем аналогично) |
| − | + | *Если же <tex> x \neq min </tex> и <tex> x \neq max </tex>, то мы должны удалить <tex>low(x)</tex> из поддерева <tex>children[high(x)]</tex>. | |
| − | *Если же x | + | Нельзя забывать, что если мы удаляем последнее вхождение <tex>x</tex>, то мы должны удалить <tex>high(x)</tex> из вспомогательного дерева. |
| − | |||
| − | |||
<pre> | <pre> | ||
| − | + | remove(T, x) | |
| − | if | + | if T.min == x and T.max == x // случай, когда в дереве один элемент |
| − | T.min = | + | T.min = none; |
| − | T. | + | return; |
| − | return | + | if T.min == x |
| − | + | if empty(T.aux) | |
| − | if (T.aux | + | T.min = T.max; |
| − | T. | + | return; |
| − | return | + | x = T.children[T.aux.min].min; |
| + | T.min = x; | ||
| + | if T.max == x | ||
| + | if empty(T.aux) | ||
| + | T.max = T.min; | ||
| + | return; | ||
else | else | ||
| − | + | x = T.children[T.aux.max].max; | |
| − | + | T.max = x; | |
| − | + | if empty(T.aux) // случай, когда элемента x нет в дереве | |
| − | + | return; | |
| − | + | remove(T.children[high(x)], low(x)); | |
| − | + | if empty(T.children[high(x)]) // если мы удалили из поддерева последний элемент | |
| − | + | remove(T.aux, high(x)); // то удаляем информацию, что это поддерево не пусто | |
| − | x = T.children[T.aux.max].max | ||
| − | T.max = x | ||
| − | if (T.aux | ||
| − | return | ||
| − | |||
| − | |||
| − | if (T.children[ | ||
| − | |||
| − | |||
</pre> | </pre> | ||
| + | |||
== next и prev == | == next и prev == | ||
Версия 01:58, 8 апреля 2012
| Определение: |
| Дерево ван Эмде Боаса — структура данных, представляющая собой дерево поиска, позволяющее хранить целые неотрицательные числа в интервале и осуществлять над ними все соответствующие дереву поиска операции. |
Проще говоря, данная структура позволяет хранить -битные числа и производить над ними операции , , , , , , и некоторые другие операции, которые присущи всем деревьям поиска.
Особенностью этой структуры является то, что все операции выполняются за , что асимптотически лучше, чем в большинстве других деревьев поиска, где — количество элементов в дереве.
Содержание
Структура
Для удобства работы с деревом будем использовать , равные степени двойки.
Как уже было сказано выше, -дерево хранит числа в интервале . Тогда 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
Удаление из дерева также делится на несколько подзадач:
- Если = = , значит в дереве один элемент, удалим его и отметим, что дерево пусто
- Если , то мы должны найти следующий минимальный элемент в этом дереве, присвоить значение второго минимального элемента и удалить его из того места, где он хранится. Второй минимум — это либо , либо (для случая действуем аналогично)
- Если же и , то мы должны удалить из поддерева .
Нельзя забывать, что если мы удаляем последнее вхождение , то мы должны удалить из вспомогательного дерева.
remove(T, x)
if T.min == x and T.max == x // случай, когда в дереве один элемент
T.min = none;
return;
if T.min == x
if empty(T.aux)
T.min = T.max;
return;
x = T.children[T.aux.min].min;
T.min = x;
if T.max == x
if empty(T.aux)
T.max = T.min;
return;
else
x = T.children[T.aux.max].max;
T.max = x;
if empty(T.aux) // случай, когда элемента x нет в дереве
return;
remove(T.children[high(x)], low(x));
if empty(T.children[high(x)]) // если мы удалили из поддерева последний элемент
remove(T.aux, high(x)); // то удаляем информацию, что это поддерево не пусто