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