Supongamos que tenemos dos pilas y ninguna otra variable temporal.
¿Es posible "construir" una estructura de datos de cola usando solo las dos pilas?
algorithm
data-structures
stack
queue
Nitina
fuente
fuente
A - Cómo revertir una pila
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.
B - Usando dos pilas como una cola
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 ladequeue
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;
Operación en cola
Operación en espera
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}
C - Implementación de la cola construida con dos pilas
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;
C - 1) Clase MyStack: una implementación de pila simple
C - 2) Clase MyQueue: Implementación de cola usando dos pilas
C - 3) Código de demostración
C - 4) Salida de muestra
fuente
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:
fuente
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.
fuente
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.
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.
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.
fuente
Una solución en c #
fuente
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 ++:
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.
fuente
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".
fuente
para el desarrollador de c # aquí está el programa completo:
fuente
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.
fuente
fuente
Una implementación de una cola usando dos pilas en Swift:
fuente
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:
De esta manera, puede crear una cola utilizando una estructura de datos de una sola pila y la pila de llamadas de recursión.
fuente
A continuación se muestra la solución en lenguaje javascript usando la sintaxis ES6.
Stack.js
QueueUsingTwoStacks.js
A continuación se muestra el uso:
index.js
fuente
stack1
. Cuando vayas dedequeue
nuevo, los moverás a elementosstack2
, 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.
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.
Son muchas funciones pequeñas, pero de las 6 funciones, 3 de ellas son solo espejos de la otra.
fuente
Aquí está mi solución en Java usando Linkedlist.
}
Nota: En este caso, la operación pop lleva mucho tiempo. Por lo tanto, no sugeriré crear una cola con dos pilas.
fuente
Con
O(1)
dequeue()
, que es lo mismo que la respuesta de pythonquick :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.Obviamente, es un buen ejercicio de codificación, ya que es ineficiente pero elegante.
fuente
** Solución JS fácil **
fuente
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
fuente
if (stack2 != null)
siempre es cierto porquestack2
se crea una instancia en el constructor.Implementación de cola usando dos objetos java.util.Stack:
fuente
return inbox.isEmpty() && outbox.isEmpty()
yreturn 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.