Дек — различия между версиями
Mutsch (обсуждение | вклад) |
Mutsch (обсуждение | вклад) |
||
Строка 1: | Строка 1: | ||
== Определение == | == Определение == | ||
[[Файл:deque1.png|thumb|right|200px|Дек]] | [[Файл:deque1.png|thumb|right|200px|Дек]] | ||
− | '''Дек''' (от англ. ''deque'' {{---}} double ended queue) {{---}} структура данных, представляющая из себя список элементов, в которой добавление новых элементов и удаление существующих производится с обоих концов. Дек можно воспринимать как двустороннюю очередь | + | '''Дек''' (от англ. ''deque'' {{---}} double ended queue) {{---}} структура данных, представляющая из себя список элементов, в которой добавление новых элементов и удаление существующих производится с обоих концов. Эта структура поддерживает как FIFO, так и LIFO. Дек можно воспринимать как двустороннюю очередь Он имеет следующие операции: |
* <tex> \mathtt{empty} </tex> {{---}} проверка на наличие элементов, | * <tex> \mathtt{empty} </tex> {{---}} проверка на наличие элементов, | ||
* <tex> \mathtt{pushBack} </tex> (запись в конец) {{---}} операция вставки нового элемента в конец, | * <tex> \mathtt{pushBack} </tex> (запись в конец) {{---}} операция вставки нового элемента в конец, | ||
Строка 8: | Строка 8: | ||
* <tex> \mathtt{popFront} </tex> (снятие с начала) {{---}} операция удаления начального элемента. | * <tex> \mathtt{popFront} </tex> (снятие с начала) {{---}} операция удаления начального элемента. | ||
− | ==Реализации== | + | == Реализации == |
Дек расходует только <tex>O(n)</tex> памяти, на хранение самих элементов. | Дек расходует только <tex>O(n)</tex> памяти, на хранение самих элементов. | ||
=== На массиве === | === На массиве === | ||
Строка 14: | Строка 14: | ||
* <tex>\mathtt{d[1\dots n]}</tex> {{---}} массив, с помощью которого реализуется дек, способный вместить не более <tex>n</tex> элементов, | * <tex>\mathtt{d[1\dots n]}</tex> {{---}} массив, с помощью которого реализуется дек, способный вместить не более <tex>n</tex> элементов, | ||
* <tex>\mathtt{d.head}</tex> {{---}} индекс головы дека, | * <tex>\mathtt{d.head}</tex> {{---}} индекс головы дека, | ||
− | * <tex>\mathtt{d.tail}</tex> {{---}} индекс хвоста | + | * <tex>\mathtt{d.tail}</tex> {{---}} индекс хвоста. |
− | Дек состоит из элементов <tex>\mathtt {d[d.tail\dots d.head]}</tex>. Всего | + | Дек состоит из элементов <tex>\mathtt {d[d.tail\dots d.head]}</tex>. Всего он способен вместить не более <tex>n</tex> элементов. В данной реализации учитывается переполнение и правильно обрабатывается изъятие из пустого дека. Недостатком является константная длина массива, хранящего элементы. Все операции выполняются за <tex>O(1)</tex>. |
− | |||
'''boolean''' empty(): | '''boolean''' empty(): | ||
Строка 46: | Строка 45: | ||
'''return''' d[d.head] | '''return''' d[d.head] | ||
− | |||
=== На саморасширяющемся массиве === | === На саморасширяющемся массиве === | ||
Ключевые поля: | Ключевые поля: | ||
Строка 52: | Строка 50: | ||
* <tex>\mathtt{newDeque[1\dots newSize]}</tex> {{---}} временный массив, где хранятся элементы после перекопирования, | * <tex>\mathtt{newDeque[1\dots newSize]}</tex> {{---}} временный массив, где хранятся элементы после перекопирования, | ||
* <tex>\mathtt{d.head}</tex> {{---}} индекс головы дека, | * <tex>\mathtt{d.head}</tex> {{---}} индекс головы дека, | ||
− | * <tex>\mathtt{d.tail}</tex> {{---}} индекс хвоста | + | * <tex>\mathtt{d.tail}</tex> {{---}} индекс хвоста, |
* <tex>\mathtt{capacity}</tex> {{---}} размер массива. | * <tex>\mathtt{capacity}</tex> {{---}} размер массива. | ||
− | Если реализовывать дек на динамическом массиве, то мы можем избежать ошибки переполнения. При выполнении операций <tex>\mathtt{pushBack}/tex> и <tex>\mathtt{pushFront}</tex> происходит проверка на переполнение и, если нужно, выделяется большее количество памяти под массив. Также | + | Если реализовывать дек на динамическом массиве, то мы можем избежать ошибки переполнения. При выполнении операций <tex>\mathtt{pushBack}/tex> и <tex>\mathtt{pushFront}</tex> происходит проверка на переполнение и, если нужно, выделяется большее количество памяти под массив. Также происходит проверка на избыточность памяти, выделенной под дек при выполнении операций <tex>\mathtt{popBack}</tex> и <tex>\mathtt{popFront}</tex>. Если памяти слишком много, то массив сокращается. Для удобства выделим в отдельную функцию <tex>\mathtt{size}</tex> получение текущего размера дека. |
'''int''' size() | '''int''' size() | ||
Строка 122: | Строка 120: | ||
* <tex>\mathtt{head}</tex> {{---}} ссылка на голову. | * <tex>\mathtt{head}</tex> {{---}} ссылка на голову. | ||
− | Дек очень просто реализуется на двусвязном списке. Элементы всегда добавляются либо в <tex>\mathtt{tail.prev}</tex>, либо в <tex>\mathtt{head.next}</tex>. В данной реализации не учитывается | + | Дек очень просто реализуется на двусвязном списке. Элементы всегда добавляются либо в <tex>\mathtt{tail.prev}</tex>, либо в <tex>\mathtt{head.next}</tex>. В данной реализации не учитывается изъятие из пустого дека. |
'''function''' pushBack(x : '''T'''): | '''function''' pushBack(x : '''T'''): | ||
Строка 147: | Строка 145: | ||
* <tex>\mathtt{rightStack}</tex> {{---}} ссылка на голову. | * <tex>\mathtt{rightStack}</tex> {{---}} ссылка на голову. | ||
− | Храним два стека - <tex>\mathtt{leftStack}</tex> и <tex>\mathtt{rightStack}</tex>. Левый стек используем для операций <tex>\mathtt{popBack}</tex> и <tex>\mathtt{pushBack | + | Храним два стека - <tex>\mathtt{leftStack}</tex> и <tex>\mathtt{rightStack}</tex>. Левый стек используем для операций <tex>\mathtt{popBack}</tex> и <tex>\mathtt{pushBack]</tex>, правый - для <tex>\mathtt{popFront}</tex> и <tex>\mathtt{pushFront}</tex>. Если мы хотим работать с левым стеком и при этом он оказывается пустым, то по очереди достаем все элементы из правого и кладем в левый. Аналогично с правым стеком. Худшее время работы каждой операции - <tex>O(n)</tex>. |
'''function''' pushBack(x : '''T'''): | '''function''' pushBack(x : '''T'''): | ||
Строка 176: | Строка 174: | ||
* [[Персистентный дек]] | * [[Персистентный дек]] | ||
− | ==Источники информации== | + | == Источники информации == |
+ | * [[wikipedia:ru:Двусвязная_очередь|Википедия {{---}} Дек (программирование)]] | ||
+ | |||
+ | [[Категория: Дискретная математика и алгоритмы]] |
Версия 08:00, 6 января 2016
Содержание
Определение
Дек (от англ. deque — double ended queue) — структура данных, представляющая из себя список элементов, в которой добавление новых элементов и удаление существующих производится с обоих концов. Эта структура поддерживает как FIFO, так и LIFO. Дек можно воспринимать как двустороннюю очередь Он имеет следующие операции:
- — проверка на наличие элементов,
- (запись в конец) — операция вставки нового элемента в конец,
- (снятие с конца) — операция удаления конечного элемента,
- (запись в начало) — операция вставки нового элемента в начало,
- (снятие с начала) — операция удаления начального элемента.
Реализации
Дек расходует только
памяти, на хранение самих элементов.На массиве
Ключевые поля:
- — массив, с помощью которого реализуется дек, способный вместить не более элементов,
- — индекс головы дека,
- — индекс хвоста.
Дек состоит из элементов
. Всего он способен вместить не более элементов. В данной реализации учитывается переполнение и правильно обрабатывается изъятие из пустого дека. Недостатком является константная длина массива, хранящего элементы. Все операции выполняются за .boolean empty(): return d.head % n + 1 == d.tail
function pushBack(x : T): if (d.head == d.tail) return error "overflow" d[d.tail] = x d.tail = (d.tail - 2 + n) % n + 1
T popBack(): if (empty()) return error "underflow" d.tail = d.tail % n + 1 return d[d.tail]
function pushFront(x : T): if (d.head == d.tail) return error "overflow" d[d.head] = x d.head = d.head % n + 1
T popFront(): if (empty()) return error "underflow" d.head = (d.head - 2 + n) % n + 1 return d[d.head]
На саморасширяющемся массиве
Ключевые поля:
- — массив, в котором хранится дек,
- — временный массив, где хранятся элементы после перекопирования,
- — индекс головы дека,
- — индекс хвоста,
- — размер массива.
Если реализовывать дек на динамическом массиве, то мы можем избежать ошибки переполнения. При выполнении операций
происходит проверка на переполнение и, если нужно, выделяется большее количество памяти под массив. Также происходит проверка на избыточность памяти, выделенной под дек при выполнении операций и . Если памяти слишком много, то массив сокращается. Для удобства выделим в отдельную функцию получение текущего размера дека.int size() if d.tail > d.head return n - d.tail + d.head - 1 else return d.head - d.tail - 1
function pushBack(x : T): if (d.head == d.tail) T newDeque[capacity * 2] for i = 1 to capacity - 1 newDeque[i] = d[d.tail + 1] d.tail = d.tail % n + 1 d = newDeque d.tail = capacity * 2 d.head = capacity - 1 capacity = capacity * 2 d[d.tail] = x d.tail = (d.tail - 2 + n) % n + 1
T popBack(): if (empty()) return error "underflow" if (size() < capacity / 4) T newDeque[capacity / 2] for i = 1 to size() newDeque[i] = d[d.tail + 1] d.tail = d.tail % n + 1 d = newDeque d.tail = capacity / 2 d.head = size() + 1 d.tail = d.tail % n + 1 return d[d.tail]
function pushFront(x : T): if (d.head == d.tail) T newDeque[capacity * 2] for i = 1 to capacity - 1 newDeque[i] = d[d.tail + 1] d.tail = d.tail % n + 1 d = newDeque d.tail = capacity * 2 d.head = capacity - 1 d[d.head] = x d.head = d.head % n + 1
T popFront(): if (empty()) return error "underflow" if (size() < capacity / 4) T newDeque[capacity / 2] for i = 1 to size() newDeque[i] = d[d.tail + 1] d.tail = d.tail % n + 1 d = newDeque d.tail = capacity / 2 d.head = size() + 1 d.head = (d.head - 2 + n) % n + 1 return d[d.head]
На списке
Ключевые поля:
-
ListItem(data : T, next : ListItem, prev : ListItem)
— конструктор, - — ссылка на хвост,
- — ссылка на голову.
Дек очень просто реализуется на двусвязном списке. Элементы всегда добавляются либо в
, либо в . В данной реализации не учитывается изъятие из пустого дека.function pushBack(x : T): tail = ListItem(x, tail, null) tail.next.prev = tail
T popBack(): data = tail.data tail = tail.next return data
function pushFront(x : T): head = ListItem(x, null, front) head.prev.next = head
T popFront(): data = head.data head = head.prev return data
На двух стеках
Ключевые поля:
- — ссылка на хвост,
- — ссылка на голову.
Храним два стека -
и . Левый стек используем для операций и , правый - для и . Если мы хотим работать с левым стеком и при этом он оказывается пустым, то по очереди достаем все элементы из правого и кладем в левый. Аналогично с правым стеком. Худшее время работы каждой операции - .function pushBack(x : T): leftStack.push(x)
T popBack(): if not leftStack.empty() return leftStack.pop() else while not rightStack.empty() leftStack.push(rightStack.pop()) return leftStack.pop()
function pushFront(x : T): rightStack.push(x)
T popFront(): if not rightStack.empty() return rightStack.pop() else while not leftStack.empty() rightStack.push(leftStack.pop()) return rightStack.pop()