Дерево ван Эмде Боаса — различия между версиями
Warrior (обсуждение | вклад)  (→find)  | 
				Warrior (обсуждение | вклад)   (→insert)  | 
				||
| Строка 56: | Строка 56: | ||
Операция вставки элемента <tex>x</tex> состоит из нескольких частей:  | Операция вставки элемента <tex>x</tex> состоит из нескольких частей:  | ||
| − | *если дерево пусто  | + | *если дерево пусто или в нем содержится единственный элемент (<tex>min</tex> = <tex>max</tex>), то присвоим полям <tex>min</tex> и <tex>max</tex> соответствующие значения. Делать что-то еще бессмысленно, так как информация записанная в <tex>min</tex> и <tex>max</tex> полностью описывает состояние текущего дерева и удовлетворяет структуре нашего дерева.  | 
*иначе:  | *иначе:  | ||
| − | **  | + | **если элемент <tex>x</tex> больше <tex>max</tex> или меньше <tex>min</tex> текущего дерева, то обновим соответствующее значение минимума или максимума, а старый минимум или максимум добавим в дерево  | 
**вставим во вспомогательное дерево <tex>aux</tex> число <tex>high(x)</tex>, если соответствующее поддерево <tex>children[high(x)]</tex> до этого было пусто  | **вставим во вспомогательное дерево <tex>aux</tex> число <tex>high(x)</tex>, если соответствующее поддерево <tex>children[high(x)]</tex> до этого было пусто  | ||
**вставим число <tex>low(x)</tex> в поддерево <tex>children[high(x)]</tex>, за исключением ситуации, когда текущее дерево {{---}} это 1-дерево, и дальнейшая вставка не требуется  | **вставим число <tex>low(x)</tex> в поддерево <tex>children[high(x)]</tex>, за исключением ситуации, когда текущее дерево {{---}} это 1-дерево, и дальнейшая вставка не требуется  | ||
| Строка 64: | Строка 64: | ||
<pre>  | <pre>  | ||
insert(T, x)  | insert(T, x)  | ||
| − |    if empty(T)   | + |    if empty(T)                                 // проверка на пустоту текущего дерева  | 
     T.min = x;  |      T.min = x;  | ||
     T.max = x;  |      T.max = x;  | ||
   else  |    else  | ||
| − |      if T.min   | + |      if T.min == T.max                         // проверка, что в дереве один элемент  | 
| − |        T.min = x;                            // релаксация минимума  | + |       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)]  | ||
</pre>  | </pre>  | ||
Версия 00:15, 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
Удаление из дерева 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