Персистентный дек
Определение: |
Дек (англ. deque — double ended queue — очередь с двумя концами) — структура данных с двусторонним доступом к элементам, т. е. их можно удалять и добавлять как в начало, так и в конец дека. |
Кроме дека ещё существует структура данных, называемая steque, которая представляет собой объединение стека и очереди - элементы можно добавлять только в один конец, а извлекать можно с обоих.
Эффективная реализация
Написать дек на массиве не представляет особого труда, зная уже, как работают стек и очередь. Далее будет приведена реализация операций добавление элемента и извлечение из одного конца дека за истинное время работы
Добавление и исключение из другого конца делается симметрично.Персистентный дек можно визуально представить как дерево, где каждый узел хранит пару - левый элемент и правый дете, а также ребёнка - ссылку на следующий дек. Только с каждым уровнем вложенности левый и правый элемент хранят в два раза больше объектов, чем на предыдущем уровне.
Тип
Pair<
> {
};
Сам дек можно инициализировать напрямую, вызвав конструктор
Deque<Pair<
>> {
left;
right;
Deque<Pair<Pair< >, Pair< >> child;
};
Таким образом, элементы с начала хранятся в левой ветке дерева, а с конца - в правой.
Наша структура данных персистентна, следовательно операция
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)
Чтобы извлечь элемент, придётся спуститься не больше, чем на глубину дерева. Аналогично для добавления. Поэтому обе операции выполняются за