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

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 2: Строка 2:
 
{{Определение
 
{{Определение
 
|definition =  
 
|definition =  
'''Дек''' (англ. deque {{---}} double ended queue {{---}} очередь с двумя концами) {{---}} структура данных с двусторонним доступом к элементам, т. е. их можно удалять и добавлять как в начало, так и в конец дека.
+
'''Дек''' (англ. deque {{---}} double ended queue {{---}} очередь с двумя концами) {{---}} структура данных с двусторонним доступом к элементам, т.е. их можно удалять и добавлять как в начало, так и в конец дека.
 
}}
 
}}
 
[[Файл:Deque.png|thumb|220px|Дек]]
 
[[Файл:Deque.png|thumb|220px|Дек]]
Кроме дека ещё существует структура данных, называемая ''steque'', которая представляет собой объединение стека и очереди - элементы можно добавлять только в один конец, а извлекать можно с обоих.
+
Кроме дека ещё существует структура данных, называемая ''steque'', которая представляет собой объединение стека и очереди - элементы можно добавлять только в один конец, а извлекать {{---}} с обоих.
  
 
== Эффективная реализация ==
 
== Эффективная реализация ==
 
[[Файл:Tree_deque.png|thumb|220px|right|Древовидная структура дека]]
 
[[Файл:Tree_deque.png|thumb|220px|right|Древовидная структура дека]]
Написать дек на массиве не представляет особого труда, зная уже, как работают стек и очередь. Далее будет приведена реализация операций добавление элемента и извлечение из одного конца дека за истинное время работы <tex> O(\log ~ n). </tex> Добавление и исключение из другого конца делается симметрично.
+
Написать дек на массиве не представляет особого труда, зная уже, как работают стек и очередь. Далее будет приведена реализация операций добавления элемента и извлечения из одного конца дека за истинное время работы <tex> O(\log ~ n). </tex> Добавление и исключение из другого конца делается симметрично.
  
 
Персистентный дек можно визуально представить как дерево, где каждый узел хранит пару - левый элемент и правый, а также ''ребёнка'' - ссылку на следующий дек. Только с каждым уровнем вложенности левый и правый элемент хранят в два раза больше объектов, чем на предыдущем уровне.
 
Персистентный дек можно визуально представить как дерево, где каждый узел хранит пару - левый элемент и правый, а также ''ребёнка'' - ссылку на следующий дек. Только с каждым уровнем вложенности левый и правый элемент хранят в два раза больше объектов, чем на предыдущем уровне.
  
 
Тип <tex>Pair</tex> хранит пару элементов <tex>first</tex> и <tex>last</tex> типов <tex>T_1</tex> и <tex>T_2</tex> соответственно.
 
Тип <tex>Pair</tex> хранит пару элементов <tex>first</tex> и <tex>last</tex> типов <tex>T_1</tex> и <tex>T_2</tex> соответственно.
<code>
+
 
 +
<code style = "display: inline-block;">
 
   Pair<<tex>T_1, ~T_2</tex>> {
 
   Pair<<tex>T_1, ~T_2</tex>> {
 
     <tex>T_1 ~first;</tex>
 
     <tex>T_1 ~first;</tex>
Строка 21: Строка 22:
 
</code>
 
</code>
  
Сам дек можно инициализировать напрямую, вызвав конструктор <tex>Deque(left, ~child, ~right)</tex>, или через шаблоны <tex>Deque<Pair<T_1, ~T_2>></tex>, тогда произойдёт следующее
+
Сам дек можно инициализировать напрямую, вызвав конструктор <tex>Deque(left, ~child, ~right)</tex>, или через шаблоны <tex>Deque<Pair<T_1, ~T_2>></tex>, тогда произойдёт следующее:
 
<code>
 
<code>
 
   Deque<Pair<<tex> T_1, ~T_2 </tex>>> {
 
   Deque<Pair<<tex> T_1, ~T_2 </tex>>> {
Строка 29: Строка 30:
 
   };
 
   };
 
</code>
 
</code>
Таким образом, элементы с начала хранятся в левой ветке дерева, а с конца - в правой.
+
Таким образом элементы с начала хранятся в левой ветке дерева, а с конца - в правой.
  
Наша структура данных персистентна, следовательно операция <tex> D.push\_front(x) </tex> возвращает новый дек <tex> D' </tex>, c элементом <tex> x </tex> в начале.
+
Наша структура данных персистентна, следовательно операция <tex> D.push\_front(x) </tex> возвращает новый дек <tex> D' </tex> c элементом <tex> x </tex> в начале.
 
<code>
 
<code>
 
   push_front(x)
 
   push_front(x)
Строка 38: Строка 39:
 
       return Deque(x, child, right)
 
       return Deque(x, child, right)
 
     else
 
     else
       // иначе объеденим его с новым элементов и попытаемся добавить в дек на следующем уровне
+
       // иначе объединим его с новым элементов и попытаемся добавить в дек на следующем уровне
 
       return Deque(<tex> \varnothing </tex>, child.push_front(Pair<x, left>), right)
 
       return Deque(<tex> \varnothing </tex>, child.push_front(Pair<x, left>), right)
 
</code>
 
</code>
Строка 49: Строка 50:
 
       return <tex> \mathcal{h} </tex>left, Deque(<tex> \varnothing </tex>, child, right)<tex> \mathcal{i} </tex>
 
       return <tex> \mathcal{h} </tex>left, Deque(<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>right, Deque(<tex> \varnothing ,~\varnothing ,~\varnothing </tex>)<tex> \mathcal{i} </tex>
 
       return <tex> \mathcal{h} </tex>right, Deque(<tex> \varnothing ,~\varnothing ,~\varnothing </tex>)<tex> \mathcal{i} </tex>
 
     else
 
     else
 
       /*  
 
       /*  
       * если два предыдущих условия оказались невыполнены, то мы рекурсивно вызываем метод pop_front()
+
       * если два предыдущих условия оказались не выполнены, то мы рекурсивно вызываем метод pop_front()
 
       * и возвращённую пару "элемент-новый дек" сохраняем в переменные temp & newDeque
 
       * и возвращённую пару "элемент-новый дек" сохраняем в переменные 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>
 
         /*
 
         /*
         * это возможно только в случае, когда в деке на максимальной глубине все элементы оказались пусты
+
         * это возможно только тогда, когда в деке на максимальной глубине все элементы оказались пусты,
         * значит мы сейчас на предпоследнем уровне, и левый ребёнок пустой, а child ссылается на абсолютно пустой дек,
+
         * значит, мы сейчас на предпоследнем уровне, левый ребёнок пустой и child ссылается на абсолютно пустой дек,
         * поэтому мы возвращаем right текущего дека, и пустой дек
+
         * поэтому возвращаем right текущего дека и пустой дек
 
         */
 
         */
 
         return <tex> \mathcal{h} </tex>right, Deque(<tex> \varnothing ,~\varnothing ,~\varnothing </tex>)<tex> \mathcal{i} </tex>
 
         return <tex> \mathcal{h} </tex>right, Deque(<tex> \varnothing ,~\varnothing ,~\varnothing </tex>)<tex> \mathcal{i} </tex>
 
       else
 
       else
 
         /*
 
         /*
         * если всё же temp не пуст, то надо вернуть первый элементы пары temp,
+
         * если всё же temp не пуст, то надо вернуть первый элементы пары temp;
         * в качестве left нового дека надо поставить temp.last (на уровне ниже, из которого он пришёл,
+
         * в качестве left нового дека надо поставить temp.last (на уровне ниже temp хранил
         * temp хранил в два раза больше элементов, поэтому на текущем уровне temp.last будет соответствовать  
+
         * в два раза больше элементов, поэтому на текущем уровне temp.last будет соответствовать  
         * как раз требуемому количеству элементов), newDeque делаем child'ом
+
         * требуемому количеству элементов); newDeque делаем child'ом
         * нового дека, а right текущего, right'ом нового
+
         * нового дека, а right текущего right'ом нового
 
         */
 
         */
 
         return <tex> \mathcal{h} </tex>temp.first, Deque(temp.last, newDeque, right)<tex> \mathcal{i} </tex>
 
         return <tex> \mathcal{h} </tex>temp.first, Deque(temp.last, newDeque, right)<tex> \mathcal{i} </tex>

Версия 16:22, 10 марта 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(x, child, right)
   else
     // иначе объединим его с новым элементов и попытаемся добавить в дек на следующем уровне
     return Deque([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([math] \varnothing [/math], child, right)[math] \mathcal{i} [/math]
   else if child == [math] \varnothing[/math]
     // если левый ребёнок оказался пуст, и при этом ссылка на следующий дек отсутствует,
     // то вернём пару из правого ребёнка и абсолютно пустого дека
     return [math] \mathcal{h} [/math]right, Deque([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([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(temp.last, newDeque, right)[math] \mathcal{i} [/math]

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

См. также

Ссылки