Как реализовать очередь с использованием двух стеков?
Предположим, что у нас есть два стека и никакая другая временная переменная.
Можно ли "построить" структуру данных очереди, используя только два стека?
Ответы
Ответ 1
Держите 2 стека, позвоните им inbox
и outbox
.
Ставить
- Нажмите новый элемент на
inbox
Dequeue
-
Если outbox
пусто, пополните его, поместив каждый элемент из inbox
и нажав его на outbox
-
Поп и верните верхний элемент из outbox
Используя этот метод, каждый элемент будет в каждом стеке ровно один раз - каждый элемент будет дважды вытолкнут и выталкивается дважды, давая амортизированные операции с постоянным временем.
Здесь реализация в Java:
public class Queue<E>
{
private Stack<E> inbox = new Stack<E>();
private Stack<E> outbox = new Stack<E>();
public void queue(E item) {
inbox.push(item);
}
public E dequeue() {
if (outbox.isEmpty()) {
while (!inbox.isEmpty()) {
outbox.push(inbox.pop());
}
}
return outbox.pop();
}
}
Ответ 2
A - Как перевернуть стек
Чтобы понять, как построить очередь, используя два стека, вы должны понимать, как можно сделать обратный стек кристально чистым. Помните, как работает стек, он очень похож на стопку посуды на вашей кухне. Последнее промытое блюдо будет на вершине чистой стеки, которое называется L ast I n F Первый O ut (LIFO) в области компьютерных наук.
Давайте представляем наш стек как бутылку, как показано ниже:
Если мы нажимаем целые числа 1,2,3 соответственно, то 3 будет на вершине стека. Поскольку 1 будет нажат первым, тогда 2 будет помещено в верхнюю часть 1. Наконец, 3 будет помещено в верхнюю часть стека, и последнее состояние нашего стека, представленного как бутылка, будет следующим:
Теперь у нас наш стек представлен как бутылка, заполненная значениями 3,2,1. И мы хотим перевернуть стек так, чтобы верхний элемент стека был 1, а нижний элемент стека - 3. Что мы можем сделать? Мы можем взять бутылку и держать ее вверх дном, чтобы все значения были в порядке по порядку?
Да, мы можем это сделать, но это бутылка. Чтобы сделать тот же процесс, нам нужно иметь второй стек, который будет хранить первые элементы стека в обратном порядке. Давайте поместим наш заполненный стек влево, а наш новый пустой стек - вправо. Чтобы изменить порядок элементов, мы собираем каждый элемент из левого стека и подталкиваем их в нужный стек. Вы можете видеть, что происходит, когда мы делаем это на изображении ниже;
Итак, мы знаем, как отменить стек.
B - Использование двух стеков как очереди
В предыдущей части я объяснил, как изменить порядок элементов стека. Это было важно, потому что, если мы будем выталкивать элементы в стек, вывод будет точно в обратном порядке очереди. Думая на примере, дайте массив целых чисел {1, 2, 3, 4, 5}
в стек. Если мы выставим элементы и напечатаем их до тех пор, пока стек не будет пуст, мы получим массив в порядке, обратном порядку нажатия, который будет {5, 4, 3, 2, 1}
. Помните, что для того же ввода, если мы отменим очередь до тех пор, пока очередь не будет пуста, выход будет {1, 2, 3, 4, 5}
. Таким образом, очевидно, что для одного и того же порядка ввода элементов вывод очереди в точности обратный выходу стека. Поскольку мы знаем, как перевернуть стек с помощью дополнительного стека, мы можем построить очередь, используя два стека.
Наша модель очереди будет состоять из двух стеков. Один стек будет использоваться для операции enqueue
(стек # 1 слева, будет вызываться как входной стек), другой стек будет использоваться для операции dequeue
(стек # 2 справа, будет вызываться как вывод Стек). Посмотрите изображение ниже;
Наш псевдокод выглядит следующим образом:
Операция запуска
Push every input element to the Input Stack
Операция Dequeue
If ( Output Stack is Empty)
pop every element in the Input Stack
and push them to the Output Stack until Input Stack is Empty
pop from Output Stack
Пусть enqueue целые числа {1, 2, 3}
соответственно. Целые элементы будут помещаться в Входной стек (Stack # 1), который находится слева;
Тогда что произойдет, если мы выполним операцию dequeue? Всякий раз, когда выполняется операция dequeue, очередь проверяет, пуст ли выходной стек или нет (см. Псевдо-код выше). Если выходной стек пуст, тогда на выходе будет извлечен входной стек, чтобы элементы входного стека будет отменено. Прежде чем возвращать значение, состояние очереди будет следующим:
Проверьте порядок элементов в стеке вывода (стек # 2). Очевидно, что мы можем вытаскивать элементы из выходного стека, чтобы результат был таким же, как если бы мы вышли из очереди. Таким образом, если мы выполним две операции dequeue, сначала получим {1, 2}
соответственно. Тогда элемент 3 будет единственным элементом выходного стека, а входной стек будет пустым. Если мы вставляем элементы 4 и 5, то состояние очереди будет следующим:
Теперь стек вывода не пуст, и если мы выполним операцию dequeue, из выпадающего стека будет выведено только 3. Тогда состояние будет показано ниже:
Опять же, если мы выполним еще две операции dequeue, в первой операции dequeue, очередь будет проверять, является ли выходной стек пустой, что верно. Затем вытащите элементы входного стека и надавите на выходной стек, если входной стек пуст, тогда состояние очереди будет следующим:
Легко видеть, вывод двух операций dequeue будет {4, 5}
C - реализация очереди, созданной двумя стеками
Вот реализация на Java. Я не буду использовать существующую реализацию Stack, поэтому пример здесь будет изобретать колесо,
C - 1) Класс MyStack: простая реализация стека
public class MyStack<T> {
// inner generic Node class
private class Node<T> {
T data;
Node<T> next;
public Node(T data) {
this.data = data;
}
}
private Node<T> head;
private int size;
public void push(T e) {
Node<T> newElem = new Node(e);
if(head == null) {
head = newElem;
} else {
newElem.next = head;
head = newElem; // new elem on the top of the stack
}
size++;
}
public T pop() {
if(head == null)
return null;
T elem = head.data;
head = head.next; // top of the stack is head.next
size--;
return elem;
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public void printStack() {
System.out.print("Stack: ");
if(size == 0)
System.out.print("Empty !");
else
for(Node<T> temp = head; temp != null; temp = temp.next)
System.out.printf("%s ", temp.data);
System.out.printf("\n");
}
}
C - 2) Класс MyQueue: реализация очереди с использованием двух стеков
public class MyQueue<T> {
private MyStack<T> inputStack; // for enqueue
private MyStack<T> outputStack; // for dequeue
private int size;
public MyQueue() {
inputStack = new MyStack<>();
outputStack = new MyStack<>();
}
public void enqueue(T e) {
inputStack.push(e);
size++;
}
public T dequeue() {
// fill out all the Input if output stack is empty
if(outputStack.isEmpty())
while(!inputStack.isEmpty())
outputStack.push(inputStack.pop());
T temp = null;
if(!outputStack.isEmpty()) {
temp = outputStack.pop();
size--;
}
return temp;
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
}
C - 3) Демо-код
public class TestMyQueue {
public static void main(String[] args) {
MyQueue<Integer> queue = new MyQueue<>();
// enqueue integers 1..3
for(int i = 1; i <= 3; i++)
queue.enqueue(i);
// execute 2 dequeue operations
for(int i = 0; i < 2; i++)
System.out.println("Dequeued: " + queue.dequeue());
// enqueue integers 4..5
for(int i = 4; i <= 5; i++)
queue.enqueue(i);
// dequeue the rest
while(!queue.isEmpty())
System.out.println("Dequeued: " + queue.dequeue());
}
}
C - 4) Выходной сигнал
Dequeued: 1
Dequeued: 2
Dequeued: 3
Dequeued: 4
Dequeued: 5
Dequeued: 1
Dequeued: 2
Dequeued: 3
Dequeued: 4
Dequeued: 5
Ответ 3
Вы можете даже моделировать очередь, используя только один стек. Второй (временный) стек может быть смоделирован стеком вызовов рекурсивных вызовов методу insert.
Принцип остается неизменным при вставке нового элемента в очередь:
- Вам нужно перенести элементы из одного стека в другой временный стек, чтобы отменить их порядок.
- Затем вставьте новый элемент, который нужно вставить, во временный стек
- Затем перенесите элементы обратно в исходный стек
- Новый элемент будет находиться в нижней части стека, а самый старый элемент - сверху (сначала нужно выскочить)
Класс Queue, использующий только один стек, будет следующим:
public class SimulatedQueue<E> {
private java.util.Stack<E> stack = new java.util.Stack<E>();
public void insert(E elem) {
if (!stack.empty()) {
E topElem = stack.pop();
insert(elem);
stack.push(topElem);
}
else
stack.push(elem);
}
public E remove() {
return stack.pop();
}
}
Ответ 4
Ответ Брайана является классически правильным. Фактически, это один из лучших способов реализации постоянных функциональных очередей с амортизированным постоянным временем. Это связано с тем, что в функциональном программировании у нас очень хороший постоянный стек (связанный список). Используя два списка в том, как описывает Брайан, можно реализовать быструю очередь, не требуя непристойного копирования.
В качестве второстепенного в стороне можно доказать, что вы можете делать все с двумя стеками. Это связано с тем, что операция с двумя стеками полностью выполняет определение универсальной машины Тьюринга. Однако, как показывает Форт, это не всегда легко.: -)
Ответ 5
Временные сложности будут хуже. Хорошая реализация очереди делает все в постоянное время.
Edit
Не знаю, почему мой ответ был опущен здесь. Если мы программируем, мы заботимся о временной сложности, и использование двух стандартных стеков для создания очереди неэффективно. Это очень важный и актуальный вопрос. Если кто-то еще почувствует необходимость уменьшить это, мне было бы интересно узнать, почему.
Немного больше: о том, почему использование двух стеков хуже, чем просто очередь: если вы используете два стека, а кто-то вызывает dequeue, когда исходный ящик пуст, вам нужно линейное время, чтобы добраться до нижней части папки "Входящие" (как вы можете видеть в коде Дейва).
Вы можете реализовать очередь как односвязный список (каждый элемент указывает на следующий вставленный элемент), сохраняя дополнительный указатель на последний вставленный элемент для push (или делая его циклическим списком). Реализация очереди и удаление из этой структуры данных очень легко сделать в постоянное время. Это наихудшее постоянное время, а не амортизация. И поскольку комментарии, похоже, требуют такого разъяснения, наихудшее постоянное время строго лучше, чем амортизированное постоянное время.
Ответ 6
Пусть очередь должна быть q и стеки, используемые для реализации q - stack1 и stack2.
q может быть реализована в двух способами:
Метод 1 (делая операцию enQueue дорогостоящей)
Этот метод гарантирует, что вновь введенный элемент всегда находится в верхней части стека 1, так что операция deQueue просто появляется из stack1. Чтобы поместить элемент в вершину stack1, используется stack2.
enQueue(q, x)
1) While stack1 is not empty, push everything from stack1 to stack2.
2) Push x to stack1 (assuming size of stacks is unlimited).
3) Push everything back to stack1.
deQueue(q)
1) If stack1 is empty then error
2) Pop an item from stack1 and return it.
Метод 2 (делая операцию deQueue дорогостоящей)
В этом методе, в операции en-queue, новый элемент вводится в верхней части stack1. В режиме ожидания очереди, если stack2 пуст, все элементы перемещаются в stack2 и, наконец, возвращается верхняя часть stack2.
enQueue(q, x)
1) Push x to stack1 (assuming size of stacks is unlimited).
deQueue(q)
1) If both stacks are empty then error.
2) If stack2 is empty
While stack1 is not empty, push everything from stack1 to stack2.
3) Pop the element from stack2 and return it.
Метод 2 определенно лучше, чем метод 1. Метод 1 перемещает все элементы дважды в операции enQueue, а метод 2 (в операции deQueue) перемещает элементы один раз и перемещает элементы только в том случае, если stack2 пуст.
Ответ 7
Решение в С#
public class Queue<T> where T : class
{
private Stack<T> input = new Stack<T>();
private Stack<T> output = new Stack<T>();
public void Enqueue(T t)
{
input.Push(t);
}
public T Dequeue()
{
if (output.Count == 0)
{
while (input.Count != 0)
{
output.Push(input.Pop());
}
}
return output.Pop();
}
}
Ответ 8
Вам нужно будет вытащить все из первого стека, чтобы получить нижний элемент. Затем верните их во второй стек для каждой операции "dequeue".
Ответ 9
Два стека в очереди определяются как stack1 и stack2.
Ставить:
Элементы euqueued всегда помещаются в stack1
Dequeue:
Верх stack2 может быть выскользнут, так как это первый элемент, вставленный в очередь, когда stack2 не пуст. Когда stack2 пуст, мы выставляем все элементы из stack1 и последовательно помещаем их в stack2. Первый элемент в очереди помещается в нижнюю часть stack1. Его можно вытащить сразу после операций popping и pushing, так как он находится на вершине stack2.
Ниже приведен пример кода С++:
template <typename T> class CQueue
{
public:
CQueue(void);
~CQueue(void);
void appendTail(const T& node);
T deleteHead();
private:
stack<T> stack1;
stack<T> stack2;
};
template<typename T> void CQueue<T>::appendTail(const T& element) {
stack1.push(element);
}
template<typename T> T CQueue<T>::deleteHead() {
if(stack2.size()<= 0) {
while(stack1.size()>0) {
T& data = stack1.top();
stack1.pop();
stack2.push(data);
}
}
if(stack2.size() == 0)
throw new exception("queue is empty");
T head = stack2.top();
stack2.pop();
return head;
}
Это решение заимствовано из моего блога. Более подробный анализ с пошаговыми имитациями операций доступен на моей веб-странице блога.
Ответ 10
для разработчика С# - это полная программа:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QueueImplimentationUsingStack
{
class Program
{
public class Stack<T>
{
public int size;
public Node<T> head;
public void Push(T data)
{
Node<T> node = new Node<T>();
node.data = data;
if (head == null)
head = node;
else
{
node.link = head;
head = node;
}
size++;
Display();
}
public Node<T> Pop()
{
if (head == null)
return null;
else
{
Node<T> temp = head;
//temp.link = null;
head = head.link;
size--;
Display();
return temp;
}
}
public void Display()
{
if (size == 0)
Console.WriteLine("Empty");
else
{
Console.Clear();
Node<T> temp = head;
while (temp!= null)
{
Console.WriteLine(temp.data);
temp = temp.link;
}
}
}
}
public class Queue<T>
{
public int size;
public Stack<T> inbox;
public Stack<T> outbox;
public Queue()
{
inbox = new Stack<T>();
outbox = new Stack<T>();
}
public void EnQueue(T data)
{
inbox.Push(data);
size++;
}
public Node<T> DeQueue()
{
if (outbox.size == 0)
{
while (inbox.size != 0)
{
outbox.Push(inbox.Pop().data);
}
}
Node<T> temp = new Node<T>();
if (outbox.size != 0)
{
temp = outbox.Pop();
size--;
}
return temp;
}
}
public class Node<T>
{
public T data;
public Node<T> link;
}
static void Main(string[] args)
{
Queue<int> q = new Queue<int>();
for (int i = 1; i <= 3; i++)
q.EnQueue(i);
// q.Display();
for (int i = 1; i < 3; i++)
q.DeQueue();
//q.Display();
Console.ReadKey();
}
}
}
Ответ 11
// Two stacks s1 Original and s2 as Temp one
private Stack<Integer> s1 = new Stack<Integer>();
private Stack<Integer> s2 = new Stack<Integer>();
/*
* Here we insert the data into the stack and if data all ready exist on
* stack than we copy the entire stack s1 to s2 recursively and push the new
* element data onto s1 and than again recursively call the s2 to pop on s1.
*
* Note here we can use either way ie We can keep pushing on s1 and than
* while popping we can remove the first element from s2 by copying
* recursively the data and removing the first index element.
*/
public void insert( int data )
{
if( s1.size() == 0 )
{
s1.push( data );
}
else
{
while( !s1.isEmpty() )
{
s2.push( s1.pop() );
}
s1.push( data );
while( !s2.isEmpty() )
{
s1.push( s2.pop() );
}
}
}
public void remove()
{
if( s1.isEmpty() )
{
System.out.println( "Empty" );
}
else
{
s1.pop();
}
}
Ответ 12
Реализация очереди с использованием двух стеков в Swift:
struct Stack<Element> {
var items = [Element]()
var count : Int {
return items.count
}
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element? {
return items.removeLast()
}
func peek() -> Element? {
return items.last
}
}
struct Queue<Element> {
var inStack = Stack<Element>()
var outStack = Stack<Element>()
mutating func enqueue(_ item: Element) {
inStack.push(item)
}
mutating func dequeue() -> Element? {
fillOutStack()
return outStack.pop()
}
mutating func peek() -> Element? {
fillOutStack()
return outStack.peek()
}
private mutating func fillOutStack() {
if outStack.count == 0 {
while inStack.count != 0 {
outStack.push(inStack.pop()!)
}
}
}
}
Ответ 13
Хотя вы получите много сообщений, связанных с реализацией очереди с двумя стеками: 1. Либо сделав процесс enQueue намного более дорогостоящим 2. Или сделав процесс deQueue намного более дорогостоящим
https://www.geeksforgeeks.org/queue-using-stacks/
Один важный способ, который я узнал из вышеприведенного поста, заключался в создании очереди только со структурой данных стека и стеком рекурсивных вызовов.
Хотя можно утверждать, что буквально это все еще использует два стека, но в идеале это использует только одну структуру данных стека.
Ниже приводится объяснение проблемы:
-
Объявите один стек для обработки и удаления данных и поместите их в стек.
-
в то время как у deQueueing есть базовое условие, при котором элемент стека выскакивает, когда размер стека равен 1. Это обеспечит отсутствие во время рекурсии deQueue.
-
При удалении очереди сначала вытолкните данные из верхней части стека. В идеале этот элемент будет элементом, который присутствует в верхней части стека. Теперь, когда это будет сделано, рекурсивно вызовите функцию deQueue, а затем вставьте элемент, расположенный выше, обратно в стек.
Код будет выглядеть ниже:
if (s1.isEmpty())
System.out.println("The Queue is empty");
else if (s1.size() == 1)
return s1.pop();
else {
int x = s1.pop();
int result = deQueue();
s1.push(x);
return result;
Таким образом, вы можете создать очередь, используя единую структуру данных стека и стек вызовов рекурсии.
Ответ 14
Я отвечу на этот вопрос в Go, потому что Go не имеет богатых коллекций в своей стандартной библиотеке.
Так как стек действительно прост в реализации, я думал, что попробую использовать два стека для выполнения очереди с двойным завершением. Чтобы лучше понять, как я пришел к моему ответу, я разделил реализацию на две части, первая часть, надеюсь, будет легче понять, но она неполна.
type IntQueue struct {
front []int
back []int
}
func (q *IntQueue) PushFront(v int) {
q.front = append(q.front, v)
}
func (q *IntQueue) Front() int {
if len(q.front) > 0 {
return q.front[len(q.front)-1]
} else {
return q.back[0]
}
}
func (q *IntQueue) PopFront() {
if len(q.front) > 0 {
q.front = q.front[:len(q.front)-1]
} else {
q.back = q.back[1:]
}
}
func (q *IntQueue) PushBack(v int) {
q.back = append(q.back, v)
}
func (q *IntQueue) Back() int {
if len(q.back) > 0 {
return q.back[len(q.back)-1]
} else {
return q.front[0]
}
}
func (q *IntQueue) PopBack() {
if len(q.back) > 0 {
q.back = q.back[:len(q.back)-1]
} else {
q.front = q.front[1:]
}
}
В основном это два стека, где мы позволяем манипулировать нижней частью стеков друг с другом. Я также использовал соглашения об именах STL, в которых традиционные операции push, pop, peek стека имеют префикс переднего/заднего, относятся ли они к передней или задней части очереди.
Проблема с вышеуказанным кодом заключается в том, что он не использует память очень эффективно. На самом деле, он растет бесконечно, пока вы не закончите пространство. Это очень плохо. Исправление для этого состоит в том, чтобы просто повторно использовать дно пространства стека, когда это возможно. Мы должны ввести смещение, чтобы отслеживать это, поскольку срез в Go не может расти спереди, как только он уменьшился.
type IntQueue struct {
front []int
frontOffset int
back []int
backOffset int
}
func (q *IntQueue) PushFront(v int) {
if q.backOffset > 0 {
i := q.backOffset - 1
q.back[i] = v
q.backOffset = i
} else {
q.front = append(q.front, v)
}
}
func (q *IntQueue) Front() int {
if len(q.front) > 0 {
return q.front[len(q.front)-1]
} else {
return q.back[q.backOffset]
}
}
func (q *IntQueue) PopFront() {
if len(q.front) > 0 {
q.front = q.front[:len(q.front)-1]
} else {
if len(q.back) > 0 {
q.backOffset++
} else {
panic("Cannot pop front of empty queue.")
}
}
}
func (q *IntQueue) PushBack(v int) {
if q.frontOffset > 0 {
i := q.frontOffset - 1
q.front[i] = v
q.frontOffset = i
} else {
q.back = append(q.back, v)
}
}
func (q *IntQueue) Back() int {
if len(q.back) > 0 {
return q.back[len(q.back)-1]
} else {
return q.front[q.frontOffset]
}
}
func (q *IntQueue) PopBack() {
if len(q.back) > 0 {
q.back = q.back[:len(q.back)-1]
} else {
if len(q.front) > 0 {
q.frontOffset++
} else {
panic("Cannot pop back of empty queue.")
}
}
}
Множество небольших функций, но из 6 функций 3 из них являются зеркалами другого.
Ответ 15
вот мое решение в java, используя связанный список.
class queue<T>{
static class Node<T>{
private T data;
private Node<T> next;
Node(T data){
this.data = data;
next = null;
}
}
Node firstTop;
Node secondTop;
void push(T data){
Node temp = new Node(data);
temp.next = firstTop;
firstTop = temp;
}
void pop(){
if(firstTop == null){
return;
}
Node temp = firstTop;
while(temp != null){
Node temp1 = new Node(temp.data);
temp1.next = secondTop;
secondTop = temp1;
temp = temp.next;
}
secondTop = secondTop.next;
firstTop = null;
while(secondTop != null){
Node temp3 = new Node(secondTop.data);
temp3.next = firstTop;
firstTop = temp3;
secondTop = secondTop.next;
}
}
}
Примечание: В этом случае поп-операция очень трудоемка. Поэтому я не буду предлагать создать очередь, используя два стека.
Ответ 16
С O(1)
dequeue()
, который аналогичен pythonquick answer:
// time: O(n), space: O(n)
enqueue(x):
if stack.isEmpty():
stack.push(x)
return
temp = stack.pop()
enqueue(x)
stack.push(temp)
// time: O(1)
x dequeue():
return stack.pop()
С O(1)
enqueue()
(это не упоминается в этом сообщении, поэтому этот ответ), в котором также используется обратное отслеживание, чтобы пузыриться и возвращать самый нижний элемент.
// O(1)
enqueue(x):
stack.push(x)
// time: O(n), space: O(n)
x dequeue():
temp = stack.pop()
if stack.isEmpty():
x = temp
else:
x = dequeue()
stack.push(temp)
return x
Очевидно, что это хорошее упражнение по кодированию, поскольку оно неэффективно, но, тем не менее, элегантно.
Ответ 17
Ниже представлено решение на языке javascript с использованием синтаксиса ES6.
Stack.js
//stack using array
class Stack {
constructor() {
this.data = [];
}
push(data) {
this.data.push(data);
}
pop() {
return this.data.pop();
}
peek() {
return this.data[this.data.length - 1];
}
size(){
return this.data.length;
}
}
export { Stack };
QueueUsingTwoStacks.js
import { Stack } from "./Stack";
class QueueUsingTwoStacks {
constructor() {
this.stack1 = new Stack();
this.stack2 = new Stack();
}
enqueue(data) {
this.stack1.push(data);
}
dequeue() {
//if both stacks are empty, return undefined
if (this.stack1.size() === 0 && this.stack2.size() === 0)
return undefined;
//if stack2 is empty, pop all elements from stack1 to stack2 till stack1 is empty
if (this.stack2.size() === 0) {
while (this.stack1.size() !== 0) {
this.stack2.push(this.stack1.pop());
}
}
//pop and return the element from stack 2
return this.stack2.pop();
}
}
export { QueueUsingTwoStacks };
Ниже использование:
index.js
import { StackUsingTwoQueues } from './StackUsingTwoQueues';
let que = new QueueUsingTwoStacks();
que.enqueue("A");
que.enqueue("B");
que.enqueue("C");
console.log(que.dequeue()); //output: "A"
Ответ 18
Реализуйте следующие операции очереди с использованием стеков.
push (x) - Переместить элемент x в конец очереди.
pop() - удаляет элемент из очереди.
peek() - получить передний элемент.
empty() - Возвращает, пуста ли очередь.
class MyQueue {
Stack<Integer> input;
Stack<Integer> output;
/** Initialize your data structure here. */
public MyQueue() {
input = new Stack<Integer>();
output = new Stack<Integer>();
}
/** Push element x to the back of queue. */
public void push(int x) {
input.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
peek();
return output.pop();
}
/** Get the front element. */
public int peek() {
if(output.isEmpty()) {
while(!input.isEmpty()) {
output.push(input.pop());
}
}
return output.peek();
}
/** Returns whether the queue is empty. */
public boolean empty() {
return input.isEmpty() && output.isEmpty();
}
}
Ответ 19
public class QueueUsingStacks<T>
{
private LinkedListStack<T> stack1;
private LinkedListStack<T> stack2;
public QueueUsingStacks()
{
stack1=new LinkedListStack<T>();
stack2 = new LinkedListStack<T>();
}
public void Copy(LinkedListStack<T> source,LinkedListStack<T> dest )
{
while(source.Head!=null)
{
dest.Push(source.Head.Data);
source.Head = source.Head.Next;
}
}
public void Enqueue(T entry)
{
stack1.Push(entry);
}
public T Dequeue()
{
T obj;
if (stack2 != null)
{
Copy(stack1, stack2);
obj = stack2.Pop();
Copy(stack2, stack1);
}
else
{
throw new Exception("Stack is empty");
}
return obj;
}
public void Display()
{
stack1.Display();
}
}
Для каждой операции enqueue мы добавляем в верхнюю часть стека1. Для каждого dequeue мы опустошаем содержимое stack1 в стек2 и удаляем элемент в верхней части стека. Сложность времени - O (n) для dequeue, так как мы должны скопировать stack1 в stack2. временная сложность очереди - это то же самое, что и обычный стек
Ответ 20
Реализация очереди с использованием двух объектов java.util.Stack:
public final class QueueUsingStacks<E> {
private final Stack<E> iStack = new Stack<>();
private final Stack<E> oStack = new Stack<>();
public void enqueue(E e) {
iStack.push(e);
}
public E dequeue() {
if (oStack.isEmpty()) {
if (iStack.isEmpty()) {
throw new NoSuchElementException("No elements present in Queue");
}
while (!iStack.isEmpty()) {
oStack.push(iStack.pop());
}
}
return oStack.pop();
}
public boolean isEmpty() {
if (oStack.isEmpty() && iStack.isEmpty()) {
return true;
}
return false;
}
public int size() {
return iStack.size() + oStack.size();
}
}