Персистентный дек — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 14: Строка 14:
 
[[Файл:Tree_deque.png|thumb|Древовидная структура дека]]
 
[[Файл:Tree_deque.png|thumb|Древовидная структура дека]]
  
Персистентный дек можно визуально представить как дерево, где каждый узел хранит пару первый элемент и последний, а также ''ребёнка'' - ссылку на следующий дек. Только с каждым уровнем вложенности левый и правый элемент представляют собой массив размера в два раза больше, чем на предыдущем уровне.
+
Персистентный дек можно визуально представить как дерево, где каждый узел хранит пару - левый элемент и правый дете, а также ''ребёнка'' - ссылку на следующий дек. Только с каждым уровнем вложенности левый и правый элемент хранят в два раза больше объектов, чем на предыдущем уровне.
  
 +
Тип <tex>Pair</tex> хранит пару элементов <tex>first</tex> и <tex>last</tex> типов <tex>T_1</tex> и <tex>T_2</tex> соответственно.
 
<code>
 
<code>
Deque<T>(first, child, last)
+
  Pair<<tex>T_1, ~T_2</tex>> {
  Deque<T> {
+
     <tex>T_1 ~first;</tex>
     T first, last;
+
     <tex>T_2 ~last;</tex>
     Deque<T> child;
 
 
   };
 
   };
 
</code>
 
</code>
  
Так как наша структура данных персистентная, то операция <tex> D.push\_front(x) </tex> возвращает новый дек <tex> D' </tex>, c элементом <tex> x </tex> в начале.
+
Сам дек можно инициализировать напрямую, вызвав конструктор <tex>Deque(left, ~child, ~right)</tex>, или через шаблоны <tex>Deque<Pair<T_1, ~T_2>></tex>, тогда произойдёт следующее
 +
<code>
 +
  Deque<Pair<<tex> T_1, ~T_2 </tex>>> {
 +
    <tex>T_1</tex> left;
 +
    <tex>T_2</tex> right;
 +
    Deque<Pair<Pair<<tex> T_1, ~T_2 </tex>>, Pair<<tex> T_1, ~T_2 </tex>>> child;
 +
  };
 +
</code>
 +
Таким образом, элементы с начала хранятся в левой ветке дерева, а с конца - в правой.
 +
 
 +
Наша структура данных персистентна, следовательно операция <tex> D.push\_front(x) </tex> возвращает новый дек <tex> D' </tex>, c элементом <tex> x </tex> в начале.
 
<code>
 
<code>
 
   push_front(x)
 
   push_front(x)
     if first == <tex> \varnothing </tex>
+
     if left == <tex> \varnothing </tex>
       return Deque<T>(x, child, last)
+
      // если левый ребенок не существует, то сделаем его новым элементом
 +
       return Deque<T>(x, child, right)
 
     else
 
     else
       return Deque<T>(<tex> \varnothing </tex>, c.push_front(Pair<x, first>), last)
+
      // иначе объеденим его с новым элементов и попытаемся добавить в дек на следующем уровне
 +
       return Deque<T>(<tex> \varnothing </tex>, child.push_front(Pair<x, left>), right)
 
</code>
 
</code>
  
Строка 36: Строка 48:
 
<code>
 
<code>
 
   pop_front()
 
   pop_front()
     if first <tex> \neq ~\varnothing</tex>
+
     if left <tex> \neq ~\varnothing</tex>
       return <tex> \mathcal{h} </tex>first, Deque<T>(<tex> \varnothing </tex>, child, last)<tex> \mathcal{i} </tex>
+
      // если левый ребёнок не пуст, то возвращаем пару из него и нового дека без левого ребёнка
 +
       return <tex> \mathcal{h} </tex>left, Deque<T>(<tex> \varnothing </tex>, child, right)<tex> \mathcal{i} </tex>
 
     else if child == <tex> \varnothing</tex>
 
     else if child == <tex> \varnothing</tex>
       return <tex> \mathcal{h} </tex>last, Deque<T>(<tex> \varnothing ,~\varnothing ,~\varnothing </tex>)<tex> \mathcal{i} </tex>
+
      // если левый ребёнок оказался пуст, и при этом ссылка на следующий дек тоже отсутсвует,
 +
      // значит, вернём пару из правого ребёнка и абсолютно пустого дека
 +
       return <tex> \mathcal{h} </tex>right, Deque<T>(<tex> \varnothing ,~\varnothing ,~\varnothing </tex>)<tex> \mathcal{i} </tex>
 
     else
 
     else
 +
      /*
 +
      * если два предыдущих условия оказались невыполнены, то мы рекурсивно вызываем метод pop_front()
 +
      * и возвращённую пару "элемент-новый дек" сохраняем в переменные temp & newDeque
 +
      * Рекурсивные вызовы прекратятся, как только левый ребёнок окажется существующим
 +
      * или в деке будет отсутствовать ссылка на следущий дек
 +
      */   
 
       temp, newDeque <tex> \leftarrow </tex> child.pop_front()
 
       temp, newDeque <tex> \leftarrow </tex> child.pop_front()
 
       if temp == <tex> \varnothing </tex>
 
       if temp == <tex> \varnothing </tex>
         return <tex> \mathcal{h} </tex>last, Deque<T>(<tex> \varnothing ,~\varnothing ,~\varnothing </tex>)<tex> \mathcal{i} </tex>
+
        /*
 +
        * это возможно только в случае, когда в деке на максимальной глубине все элементы оказались пусты
 +
        * значит мы сейчас на предпоследнем уровне, и левый ребёнок пустой, а child ссылается на абсолютно пустой дек,
 +
        * поэтому мы возвращаем right текущего дека, и пустой дек
 +
        */
 +
         return <tex> \mathcal{h} </tex>right, Deque<T>(<tex> \varnothing ,~\varnothing ,~\varnothing </tex>)<tex> \mathcal{i} </tex>
 
       else
 
       else
         return <tex> \mathcal{h} </tex>temp.first, Deque<T>(temp.child, newDeque, last)<tex> \mathcal{i} </tex>
+
        /*
 +
        * если всё же temp не пуст, то надо вернуть первый элементы пары temp,
 +
        * в качестве left нового дека надо поставить temp.last (на уровне ниже, из которого он пришёл, temp хранил в два раза больше элементов
 +
        * поэтому на текущем уровне temp.last будет соответствовать как раз требуемому количеству элементов), newDeque делаем child'ом
 +
        * нового дека, а right текущего, right'ом нового
 +
        */
 +
         return <tex> \mathcal{h} </tex>temp.first, Deque<T>(temp.last, newDeque, right)<tex> \mathcal{i} </tex>
 
</code>
 
</code>
  

Версия 20:10, 9 марта 2012

Эта статья находится в разработке!


Определение:
Дек (англ. deque — double ended queue — очередь с двумя концами) — структура данных с двусторонним доступом к элементам, т. е. их можно удалять и добавлять как в начало, так и в конец дека.
Дек

Кроме дека ещё существует структура данных, называемая steque, которая представляет собой объединение стека и очереди - элементы можно добавлять только в один конец, а извлекать можно с обоих.

Эффективная реализация

Написать дек на массиве не представляет особого труда, зная уже, как работают стек и очередь. Далее будет приведена реализация операций добавление элемента и извлечение из одного конца дека за истинное время работы [math] O(\log ~ n). [/math] Добавление и исключение из другого конца делается симметрично.

Древовидная структура дека

Персистентный дек можно визуально представить как дерево, где каждый узел хранит пару - левый элемент и правый дете, а также ребёнка - ссылку на следующий дек. Только с каждым уровнем вложенности левый и правый элемент хранят в два раза больше объектов, чем на предыдущем уровне.

Тип [math]Pair[/math] хранит пару элементов [math]first[/math] и [math]last[/math] типов [math]T_1[/math] и [math]T_2[/math] соответственно.

 Pair<[math]T_1, ~T_2[/math]> {
   [math]T_1 ~first;[/math]
   [math]T_2 ~last;[/math]
 };

Сам дек можно инициализировать напрямую, вызвав конструктор [math]Deque(left, ~child, ~right)[/math], или через шаблоны [math]Deque\lt Pair\lt T_1, ~T_2\gt \gt [/math], тогда произойдёт следующее

 Deque<Pair<[math] T_1, ~T_2 [/math]>> {
   [math]T_1[/math] left;
   [math]T_2[/math] right;
   Deque<Pair<Pair<[math] T_1, ~T_2 [/math]>, Pair<[math] T_1, ~T_2 [/math]>> child;
 };

Таким образом, элементы с начала хранятся в левой ветке дерева, а с конца - в правой.

Наша структура данных персистентна, следовательно операция [math] D.push\_front(x) [/math] возвращает новый дек [math] D' [/math], c элементом [math] x [/math] в начале.

 push_front(x)
   if left == [math] \varnothing [/math]
     // если левый ребенок не существует, то сделаем его новым элементом
     return Deque<T>(x, child, right)
   else
     // иначе объеденим его с новым элементов и попытаемся добавить в дек на следующем уровне
     return Deque<T>([math] \varnothing [/math], child.push_front(Pair<x, left>), right)

Метод [math] D.pop\_front() [/math] возвращает пару [math] \left \langle e, ~D' \right \rangle [/math] из первого элемента и нового дека, полученного из старого изъятием этого элемента.

 pop_front()
   if left [math] \neq ~\varnothing[/math]
     // если левый ребёнок не пуст, то возвращаем пару из него и нового дека без левого ребёнка
     return [math] \mathcal{h} [/math]left, Deque<T>([math] \varnothing [/math], child, right)[math] \mathcal{i} [/math]
   else if child == [math] \varnothing[/math]
     // если левый ребёнок оказался пуст, и при этом ссылка на следующий дек тоже отсутсвует,
     // значит, вернём пару из правого ребёнка и абсолютно пустого дека
     return [math] \mathcal{h} [/math]right, Deque<T>([math] \varnothing ,~\varnothing ,~\varnothing [/math])[math] \mathcal{i} [/math]
   else
     /* 
      * если два предыдущих условия оказались невыполнены, то мы рекурсивно вызываем метод pop_front()
      * и возвращённую пару "элемент-новый дек" сохраняем в переменные temp & newDeque
      * Рекурсивные вызовы прекратятся, как только левый ребёнок окажется существующим 
      * или в деке будет отсутствовать ссылка на следущий дек
      */     
     temp, newDeque [math] \leftarrow [/math] child.pop_front()
     if temp == [math] \varnothing [/math]
       /*
        * это возможно только в случае, когда в деке на максимальной глубине все элементы оказались пусты
        * значит мы сейчас на предпоследнем уровне, и левый ребёнок пустой, а child ссылается на абсолютно пустой дек,
        * поэтому мы возвращаем right текущего дека, и пустой дек
        */
       return [math] \mathcal{h} [/math]right, Deque<T>([math] \varnothing ,~\varnothing ,~\varnothing [/math])[math] \mathcal{i} [/math]
     else
       /*
        * если всё же temp не пуст, то надо вернуть первый элементы пары temp, 
        * в качестве left нового дека надо поставить temp.last (на уровне ниже, из которого он пришёл, temp хранил в два раза больше элементов
        * поэтому на текущем уровне temp.last будет соответствовать как раз требуемому количеству элементов), newDeque делаем child'ом
        * нового дека, а right текущего, right'ом нового
        */
       return [math] \mathcal{h} [/math]temp.first, Deque<T>(temp.last, newDeque, right)[math] \mathcal{i} [/math]

Чтобы извлечь элемент, придётся спуститься не больше, чем на глубину дерева. Аналогично для добавления. Поэтому обе операции выполняются за [math] O(\log ~n) [/math]

См. также

Ссылки