This commit is contained in:
krahets
2026-04-03 18:46:15 +08:00
parent 377736b1bd
commit 9d21ca86b0
352 changed files with 46563 additions and 11262 deletions

View File

@@ -4,7 +4,7 @@ comments: true
# 5.3   Двусторонняя очередь
В очереди мы можем удалять элементы только из головы или добавлять их только в хвост. Как показано на рисунке 5-7, <u>двусторонняя очередь (double-ended queue)</u> обеспечивает более высокую гибкость и позволяет выполнять добавление и удаление элементов как с головы, так и с хвоста.
В обычной очереди мы можем удалять элементы только из головы и добавлять их только в хвост. Как показано на рисунке 5-7, <u>двусторонняя очередь (double-ended queue)</u> обеспечивает большую гибкость и позволяет выполнять добавление и удаление элементов как с головы, так и с хвоста.
![Операции двусторонней очереди](deque.assets/deque_operations.png){ class="animation-figure" }
@@ -403,25 +403,25 @@ comments: true
### 1. &nbsp; Реализация на основе двусвязного списка
Вспомним предыдущий раздел: там мы использовали обычный односвязный список для реализации очереди, потому что он позволяет удобно удалять головной узелто соответствует операции dequeue) и добавлять новый узел после хвостового узлато соответствует операции enqueue).
Вспомним предыдущий раздел: там мы использовали обычный односвязный список для реализации очереди, потому что он позволяет удобно удалять головной узел, что соответствует операции `dequeue` , и добавлять новый узел после хвостового узла, что соответствует операции `enqueue` .
Для двусторонней очереди и голова, и хвост допускают операции добавления и удаления элементов. Иначе говоря, двусторонняя очередь требует реализации еще одного симметричного направления операций. Поэтому в качестве базовой структуры данных двусторонней очереди мы используем "двусвязный список".
Для двусторонней очереди и голова, и хвост допускают операции добавления и удаления элементов. Иначе говоря, двусторонняя очередь требует реализации еще одного симметричного направления операций. Поэтому в качестве базовой структуры данных двусторонней очереди удобно использовать двусвязный список.
Как показано на рисунках ниже, мы рассматриваем головной и хвостовой узлы двусвязного списка как голову и хвост двусторонней очереди и одновременно реализуем функции добавления и удаления узлов с обеих сторон.
=== "LinkedListDeque"
=== "<1>"
![Операции enqueue и dequeue для двусторонней очереди на связном списке](deque.assets/linkedlist_deque_step1.png){ class="animation-figure" }
=== "push_last()"
=== "<2>"
![linkedlist_deque_push_last](deque.assets/linkedlist_deque_step2_push_last.png){ class="animation-figure" }
=== "push_first()"
=== "<3>"
![linkedlist_deque_push_first](deque.assets/linkedlist_deque_step3_push_first.png){ class="animation-figure" }
=== "pop_last()"
=== "<4>"
![linkedlist_deque_pop_last](deque.assets/linkedlist_deque_step4_pop_last.png){ class="animation-figure" }
=== "pop_first()"
=== "<5>"
![linkedlist_deque_pop_first](deque.assets/linkedlist_deque_step5_pop_first.png){ class="animation-figure" }
<p align="center"> Рисунок 5-8 &nbsp; Операции enqueue и dequeue для двусторонней очереди на связном списке </p>
@@ -2025,7 +2025,7 @@ comments: true
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
=end
### Узел двусвязного списка
# ## Узел двусвязного списка
class ListNode
attr_accessor :val
attr_accessor :next # Ссылка на узел-преемник
@@ -2155,26 +2155,26 @@ comments: true
### 2. &nbsp; Реализация на основе массива
Как показано на рисунках ниже, аналогично реализации очереди на массиве мы также можем использовать кольцевой массив для реализации двусторонней очереди.
Как показано на рисунках ниже, аналогично реализации обычной очереди на массиве мы также можем использовать кольцевой массив для реализации двусторонней очереди.
=== "ArrayDeque"
=== "<1>"
![Операции enqueue и dequeue для двусторонней очереди на массиве](deque.assets/array_deque_step1.png){ class="animation-figure" }
=== "push_last()"
=== "<2>"
![array_deque_push_last](deque.assets/array_deque_step2_push_last.png){ class="animation-figure" }
=== "push_first()"
=== "<3>"
![array_deque_push_first](deque.assets/array_deque_step3_push_first.png){ class="animation-figure" }
=== "pop_last()"
=== "<4>"
![array_deque_pop_last](deque.assets/array_deque_step4_pop_last.png){ class="animation-figure" }
=== "pop_first()"
=== "<5>"
![array_deque_pop_first](deque.assets/array_deque_step5_pop_first.png){ class="animation-figure" }
<p align="center"> Рисунок 5-9 &nbsp; Операции enqueue и dequeue для двусторонней очереди на массиве </p>
На основе реализации обычной очереди нужно лишь добавить методы "enqueue в голову" и "dequeue из хвоста":
На основе реализации обычной очереди нужно лишь добавить методы добавления в голову очереди и удаления из хвоста:
=== "Python"
@@ -3605,4 +3605,4 @@ comments: true
Двусторонняя очередь сочетает в себе логику стека и очереди, **поэтому она может покрыть все сценарии применения обеих структур и при этом предоставляет более высокую степень свободы**.
Мы знаем, что функция "undo" в программном обеспечении обычно реализуется с помощью стека: система `push`-ит каждое изменение в стек, а затем использует `pop` для отмены. Однако, учитывая ограниченность системных ресурсов, программы обычно ограничивают число шагов отмены (например, разрешают хранить только $50$ шагов). Когда длина стека превышает $50$, программе нужно удалить элемент с дна стека (то есть с головы очереди). **Но стек не может реализовать такую операцию, и в этом случае его приходится заменять двусторонней очередью**. Обрати внимание: основная логика "undo" по-прежнему следует стековому правилу LIFO, просто двусторонняя очередь позволяет более гибко реализовать некоторые дополнительные механизмы.
Мы знаем, что функция "undo" в программном обеспечении обычно реализуется с помощью стека: система помещает каждое изменение в стек с помощью `push` , а затем использует `pop` для отмены. Однако, учитывая ограниченность системных ресурсов, программы обычно ограничивают число шагов отмены, например разрешают хранить только $50$ шагов. Когда длина стека превышает этот предел, программе нужно удалить элемент с дна стека, то есть с головы очереди. **Но стек не может реализовать такую операцию, и в этом случае его приходится заменять двусторонней очередью**. Обрати внимание: основная логика "undo" по-прежнему следует стековому правилу LIFO, просто двусторонняя очередь позволяет более гибко реализовать некоторые дополнительные механизмы.