Respuestas:
Mantenga 2 pilas, llamémoslas inbox
y outbox
.
Poner en cola :
inbox
Dequeue :
Si outbox
está vacío, rellénelo sacando cada elemento inbox
y empujándolo haciaoutbox
Pop y devolver el elemento superior de outbox
Usando este método, cada elemento estará en cada pila exactamente una vez, lo que significa que cada elemento se empujará dos veces y se abrirá dos veces, dando operaciones de tiempo constante amortizado.
Aquí hay una implementación en 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();
}
}
Para entender cómo construir una cola usando dos pilas, debes entender cómo revertir una pila cristalina. Recuerde cómo funciona la pila, es muy similar a la pila de platos en su cocina. El último plato lavado estará en la parte superior de la pila limpia, que se llama L ast I n F firstst O ut (LIFO) en informática.
Imaginemos nuestra pila como una botella como se muestra a continuación;
Si empujamos enteros 1,2,3 respectivamente, entonces 3 estará en la parte superior de la pila. Como 1 será empujado primero, luego 2 se colocará en la parte superior de 1. Por último, 3 se colocará en la parte superior de la pila y el último estado de nuestra pila representado como una botella será el siguiente;
Ahora tenemos nuestra pila representada como una botella que se rellena con valores 3,2,1. Y queremos invertir la pila para que el elemento superior de la pila sea 1 y el elemento inferior de la pila sea 3. ¿Qué podemos hacer? ¿Podemos tomar la botella y mantenerla boca abajo para que todos los valores se inviertan en orden?
Sí, podemos hacer eso, pero eso es una botella. Para hacer el mismo proceso, necesitamos tener una segunda pila que almacene los primeros elementos de la pila en orden inverso. Pongamos nuestra pila poblada a la izquierda y nuestra nueva pila vacía a la derecha. Para invertir el orden de los elementos, vamos a hacer estallar cada elemento de la pila izquierda y empujarlos a la pila derecha. Puedes ver lo que sucede mientras lo hacemos en la imagen a continuación;
Entonces sabemos cómo invertir una pila.
En la parte anterior, he explicado cómo podemos invertir el orden de los elementos de la pila. Esto era importante, porque si empujamos y sacamos elementos a la pila, el resultado será exactamente en orden inverso al de una cola. Pensando en un ejemplo, empujemos la matriz de enteros {1, 2, 3, 4, 5}
a una pila. Si hacemos estallar los elementos e imprimimos hasta que la pila esté vacía, obtendremos la matriz en el orden inverso al orden de inserción, que será {5, 4, 3, 2, 1}
Recuerde que para la misma entrada, si retiramos la cola hasta que la cola esté vacía, la salida será {1, 2, 3, 4, 5}
. Por lo tanto, es obvio que para el mismo orden de entrada de elementos, la salida de la cola es exactamente inversa a la salida de una pila. Como sabemos cómo invertir una pila usando una pila adicional, podemos construir una cola usando dos pilas.
Nuestro modelo de cola consistirá en dos pilas. Una pila se usará para la enqueue
operación (la pila # 1 a la izquierda, se llamará como Pila de entrada), otra pila se usará para la dequeue
operación (la pila # 2 a la derecha, se llamará como Pila de salida). Mira la imagen a continuación;
Nuestro pseudocódigo es el siguiente;
Push every input element to the Input Stack
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
Pongamos en cola los enteros {1, 2, 3}
respectivamente. Los enteros se insertarán en la Pila de entrada ( Pila # 1 ) que se encuentra a la izquierda;
Entonces, ¿qué sucederá si ejecutamos una operación de retirada de cola? Cada vez que se ejecuta una operación de extracción, la cola verificará si la Pila de salida está vacía o no (consulte el pseudocódigo anterior) Si la Pila de salida está vacía, la Pila de entrada se extraerá en la salida para que los elementos de la pila de entrada se invertirá. Antes de devolver un valor, el estado de la cola será el siguiente;
Verifique el orden de los elementos en la Pila de salida (Pila # 2). Es obvio que podemos extraer los elementos de la Pila de salida para que la salida sea la misma que si saliéramos de una cola. Por lo tanto, si ejecutamos dos operaciones en cola, primero obtendremos {1, 2}
respectivamente. Entonces el elemento 3 será el único elemento de la Pila de salida, y la Pila de entrada estará vacía. Si ponemos en cola los elementos 4 y 5, entonces el estado de la cola será el siguiente;
Ahora la pila de salida no está vacía, y si ejecutamos una operación de retirada de la cola, solo 3 saldrán de la pila de salida. Entonces el estado se verá como a continuación;
Nuevamente, si ejecutamos dos operaciones más de cola, en la primera operación de cola, la cola verificará si la Pila de Salida está vacía, lo cual es cierto. Luego extraiga los elementos de la Pila de entrada y empújelos a la Pila de salida hasta que la Pila de entrada esté vacía, luego el estado de la Cola será el siguiente;
Fácil de ver, la salida de las dos operaciones de eliminación de cola será {4, 5}
Aquí hay una implementación en Java. No voy a usar la implementación existente de Stack, así que el ejemplo aquí va a reinventar la rueda;
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");
}
}
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;
}
}
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());
}
}
Dequeued: 1
Dequeued: 2
Dequeued: 3
Dequeued: 4
Dequeued: 5
Incluso puede simular una cola usando solo una pila. La segunda pila (temporal) se puede simular mediante la pila de llamadas de llamadas recursivas al método de inserción.
El principio permanece igual al insertar un nuevo elemento en la cola:
Una clase de cola que usa solo una pila, sería la siguiente:
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();
}
}
n items
en la cola usando la estructura de datos anterior. la suma (1 + 2 + 4 + 8 + .... + 2(n-1))
resulta en ~O(n^2)
. Espero que entiendas el punto.
Sin embargo, la complejidad del tiempo sería peor. Una buena implementación de la cola hace todo en tiempo constante.
Editar
No estoy seguro de por qué mi respuesta ha sido rechazada aquí. Si programamos, nos preocupamos por la complejidad del tiempo, y el uso de dos pilas estándar para hacer una cola es ineficiente. Es un punto muy válido y relevante. Si alguien más siente la necesidad de menospreciar esto, me interesaría saber por qué.
Un poco más de detalle : sobre por qué usar dos pilas es peor que solo una cola: si usa dos pilas y alguien llama a la espera mientras la bandeja de salida está vacía, necesita tiempo lineal para llegar al fondo de la bandeja de entrada (como puede ver en el código de Dave).
Puede implementar una cola como una lista enlazada individualmente (cada elemento apunta al siguiente elemento insertado), manteniendo un puntero adicional al último elemento insertado para los empujes (o haciéndolo una lista cíclica). Implementar cola y cola en esta estructura de datos es muy fácil de hacer en tiempo constante. Ese es el peor tiempo constante, no amortizado. Y, como los comentarios parecen pedir esta aclaración, el tiempo constante en el peor de los casos es estrictamente mejor que el tiempo constante amortizado.
Deje que la cola a implementar sea q y las pilas utilizadas para implementar q sean stack1 y stack2.
q se puede implementar de dos maneras:
Método 1 (haciendo que la operación enQueue sea costosa)
Este método asegura que el elemento recién ingresado esté siempre en la parte superior de la pila 1, de modo que la operación deQueue simplemente aparezca en la pila1. Para colocar el elemento en la parte superior de stack1, se utiliza 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.
Método 2 (haciendo que la operación deQueue sea costosa)
En este método, en la operación en cola, el nuevo elemento se ingresa en la parte superior de stack1. En la operación de eliminación de la cola, si stack2 está vacío, todos los elementos se mueven a stack2 y finalmente se devuelve la parte superior de 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.
El método 2 es definitivamente mejor que el método 1. El método 1 mueve todos los elementos dos veces en la operación enQueue, mientras que el método 2 (en la operación deQueue) mueve los elementos una vez y mueve los elementos solo si stack2 está vacío.
Una solución en c #
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();
}
}
Dos pilas en la cola se definen como stack1 y stack2 .
Poner en cola: los elementos euqueued siempre se insertan en stack1
Dequeue: la parte superior de stack2 se puede extraer ya que es el primer elemento insertado en la cola cuando stack2 no está vacío. Cuando stack2 está vacío, sacamos todos los elementos de stack1 y los empujamos a stack2 uno por uno. El primer elemento de una cola se inserta en la parte inferior de stack1 . Se puede extraer directamente después de explotar y empujar operaciones ya que está en la parte superior de stack2 .
El siguiente es el mismo código de muestra de C ++:
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;
}
Esta solución está tomada de mi blog . Un análisis más detallado con simulaciones de operación paso a paso está disponible en la página web de mi blog.
Tendrás que sacar todo de la primera pila para obtener el elemento inferior. Luego, vuelva a colocarlos en la segunda pila para cada operación de "eliminación de cola".
para el desarrollador de c # aquí está el programa completo:
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();
}
}
}
Implemente las siguientes operaciones de una cola usando pilas.
push (x): empuja el elemento x hacia el final de la cola.
pop () - Elimina el elemento delante de la cola.
peek () - Obtiene el elemento frontal.
empty () - Devuelve si la cola está vacía.
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();
}
}
// 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();
}
}
Una implementación de una cola usando dos pilas en 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()!)
}
}
}
}
Si bien obtendrá muchas publicaciones relacionadas con la implementación de una cola con dos pilas: 1. O bien haciendo que el proceso enQueue sea mucho más costoso 2. O haciendo que el proceso deQueue sea mucho más costoso
https://www.geeksforgeeks.org/queue-using-stacks/
Una forma importante que descubrí en la publicación anterior fue construir una cola con solo la estructura de datos de la pila y la pila de llamadas de recursión.
Si bien uno puede argumentar que, literalmente, esto todavía está usando dos pilas, pero idealmente esto está usando solo una estructura de datos de pila.
A continuación se muestra la explicación del problema:
Declare una sola pila para enQueuing y deQueuing de los datos e inserte los datos en la pila.
mientras que la eliminación de la cola tiene una condición básica en la que el elemento de la pila se apila cuando el tamaño de la pila es 1. Esto asegurará que no haya desbordamiento de la pila durante la recursión deQueue.
Mientras se quita la cola, primero saque los datos de la parte superior de la pila. Idealmente, este elemento será el elemento que está presente en la parte superior de la pila. Ahora, una vez hecho esto, llame de forma recursiva a la función deQueue y luego empuje el elemento que aparece arriba nuevamente en la pila.
El código se verá a continuación:
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;
De esta manera, puede crear una cola utilizando una estructura de datos de una sola pila y la pila de llamadas de recursión.
A continuación se muestra la solución en lenguaje javascript usando la sintaxis 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 };
A continuación se muestra el uso:
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"
stack1
. Cuando vayas de dequeue
nuevo, los moverás a elementos stack2
, colocándolos por delante de lo que ya estaba allí.
Contestaré esta pregunta en Go porque Go no tiene muchas colecciones ricas en su biblioteca estándar.
Como una pila es realmente fácil de implementar, pensé en intentar usar dos pilas para lograr una cola de doble finalización. Para comprender mejor cómo llegué a mi respuesta, he dividido la implementación en dos partes, espero que la primera parte sea más fácil de entender, pero está incompleta.
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:]
}
}
Básicamente son dos pilas donde permitimos que la parte inferior de las pilas sea manipulada entre sí. También utilicé las convenciones de nomenclatura STL, donde las operaciones tradicionales de inserción, extracción y peek de una pila tienen un prefijo frontal / posterior, ya sea que se refieran al frente o al reverso de la cola.
El problema con el código anterior es que no usa la memoria de manera muy eficiente. En realidad, crece sin cesar hasta que te quedas sin espacio. Es realmente malo. La solución para esto es simplemente reutilizar la parte inferior del espacio de la pila siempre que sea posible. Tenemos que introducir un desplazamiento para rastrear esto, ya que un segmento en Go no puede crecer en el frente una vez encogido.
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.")
}
}
}
Son muchas funciones pequeñas, pero de las 6 funciones, 3 de ellas son solo espejos de la otra.
Aquí está mi solución en Java usando Linkedlist.
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;
}
}
}
Nota: En este caso, la operación pop lleva mucho tiempo. Por lo tanto, no sugeriré crear una cola con dos pilas.
Con O(1)
dequeue()
, que es lo mismo que la respuesta de pythonquick :
// 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()
Con O(1)
enqueue()
(esto no se menciona en esta publicación, por lo que esta respuesta), que también utiliza el retroceso para burbujear y devolver el elemento más inferior.
// 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
Obviamente, es un buen ejercicio de codificación, ya que es ineficiente pero elegante.
** Solución JS fácil **
/*
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.
*/
class myQueue {
constructor() {
this.stack1 = [];
this.stack2 = [];
}
push(item) {
this.stack1.push(item)
}
remove() {
if (this.stack1.length == 0 && this.stack2.length == 0) {
return "Stack are empty"
}
if (this.stack2.length == 0) {
while (this.stack1.length != 0) {
this.stack2.push(this.stack1.pop())
}
}
return this.stack2.pop()
}
peek() {
if (this.stack2.length == 0 && this.stack1.length == 0) {
return 'Empty list'
}
if (this.stack2.length == 0) {
while (this.stack1.length != 0) {
this.stack2.push(this.stack1.pop())
}
}
return this.stack2[0]
}
isEmpty() {
return this.stack2.length === 0 && this.stack1.length === 0;
}
}
const q = new myQueue();
q.push(1);
q.push(2);
q.push(3);
q.remove()
console.log(q)
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();
}
}
Para cada operación en cola, agregamos a la parte superior de la pila1. Para cada dequeue, vaciamos el contenido de stack1 en stack2, y eliminamos el elemento en la parte superior de la pila. La complejidad del tiempo es O (n) para dequeue, ya que tenemos que copiar el stack1 a stack2. La complejidad temporal de la cola es la misma que la de una pila normal
if (stack2 != null)
siempre es cierto porque stack2
se crea una instancia en el constructor.
Implementación de cola usando dos objetos 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();
}
}
return inbox.isEmpty() && outbox.isEmpty()
y return inbox.size() + outbox.size()
, respectivamente. El código de Dave L. ya arroja una excepción cuando se retira de una cola vacía. La pregunta original ni siquiera era sobre Java; se trataba de estructuras de datos / algoritmos en general. La implementación de Java fue solo una ilustración adicional.