Персистентный дек — различия между версиями
Shersh (обсуждение | вклад) |
Shersh (обсуждение | вклад) |
||
| Строка 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> | ||
| − | + | Pair<<tex>T_1, ~T_2</tex>> { | |
| − | + | <tex>T_1 ~first;</tex> | |
| − | + | <tex>T_2 ~last;</tex> | |
| − | |||
}; | }; | ||
</code> | </code> | ||
| − | + | Сам дек можно инициализировать напрямую, вызвав конструктор <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 | + | if left == <tex> \varnothing </tex> |
| − | return Deque<T>(x, child, | + | // если левый ребенок не существует, то сделаем его новым элементом |
| + | return Deque<T>(x, child, right) | ||
else | else | ||
| − | return Deque<T>(<tex> \varnothing </tex>, | + | // иначе объеденим его с новым элементов и попытаемся добавить в дек на следующем уровне |
| + | return Deque<T>(<tex> \varnothing </tex>, child.push_front(Pair<x, left>), right) | ||
</code> | </code> | ||
| Строка 36: | Строка 48: | ||
<code> | <code> | ||
pop_front() | pop_front() | ||
| − | if | + | if left <tex> \neq ~\varnothing</tex> |
| − | return <tex> \mathcal{h} </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> | + | // если левый ребёнок оказался пуст, и при этом ссылка на следующий дек тоже отсутсвует, |
| + | // значит, вернём пару из правого ребёнка и абсолютно пустого дека | ||
| + | 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> | + | /* |
| + | * это возможно только в случае, когда в деке на максимальной глубине все элементы оказались пусты | ||
| + | * значит мы сейчас на предпоследнем уровне, и левый ребёнок пустой, а 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. | + | /* |
| + | * если всё же 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, которая представляет собой объединение стека и очереди - элементы можно добавлять только в один конец, а извлекать можно с обоих.
Эффективная реализация
Написать дек на массиве не представляет особого труда, зная уже, как работают стек и очередь. Далее будет приведена реализация операций добавление элемента и извлечение из одного конца дека за истинное время работы Добавление и исключение из другого конца делается симметрично.
Персистентный дек можно визуально представить как дерево, где каждый узел хранит пару - левый элемент и правый дете, а также ребёнка - ссылку на следующий дек. Только с каждым уровнем вложенности левый и правый элемент хранят в два раза больше объектов, чем на предыдущем уровне.
Тип хранит пару элементов и типов и соответственно.
Pair<> { };
Сам дек можно инициализировать напрямую, вызвав конструктор , или через шаблоны , тогда произойдёт следующее
Deque<Pair<>> { left; right; Deque<Pair<Pair<>, Pair<>> child; };
Таким образом, элементы с начала хранятся в левой ветке дерева, а с конца - в правой.
Наша структура данных персистентна, следовательно операция возвращает новый дек , c элементом в начале.
push_front(x) if left == // если левый ребенок не существует, то сделаем его новым элементом return Deque<T>(x, child, right) else // иначе объеденим его с новым элементов и попытаемся добавить в дек на следующем уровне return Deque<T>(, child.push_front(Pair<x, left>), right)
Метод возвращает пару из первого элемента и нового дека, полученного из старого изъятием этого элемента.
pop_front() if left // если левый ребёнок не пуст, то возвращаем пару из него и нового дека без левого ребёнка return left, Deque<T>(, child, right) else if child == // если левый ребёнок оказался пуст, и при этом ссылка на следующий дек тоже отсутсвует, // значит, вернём пару из правого ребёнка и абсолютно пустого дека return right, Deque<T>() else /* * если два предыдущих условия оказались невыполнены, то мы рекурсивно вызываем метод pop_front() * и возвращённую пару "элемент-новый дек" сохраняем в переменные temp & newDeque * Рекурсивные вызовы прекратятся, как только левый ребёнок окажется существующим * или в деке будет отсутствовать ссылка на следущий дек */ temp, newDeque child.pop_front() if temp == /* * это возможно только в случае, когда в деке на максимальной глубине все элементы оказались пусты * значит мы сейчас на предпоследнем уровне, и левый ребёнок пустой, а child ссылается на абсолютно пустой дек, * поэтому мы возвращаем right текущего дека, и пустой дек */ return right, Deque<T>() else /* * если всё же temp не пуст, то надо вернуть первый элементы пары temp, * в качестве left нового дека надо поставить temp.last (на уровне ниже, из которого он пришёл, temp хранил в два раза больше элементов * поэтому на текущем уровне temp.last будет соответствовать как раз требуемому количеству элементов), newDeque делаем child'ом * нового дека, а right текущего, right'ом нового */ return temp.first, Deque<T>(temp.last, newDeque, right)
Чтобы извлечь элемент, придётся спуститься не больше, чем на глубину дерева. Аналогично для добавления. Поэтому обе операции выполняются за