Risposte:
Mantieni 2 pile, chiamiamole inbox
e outbox
.
Accodare :
inbox
Dequeue :
Se outbox
è vuoto, riempilo facendo scoppiare ogni elemento inbox
e spingendolooutbox
Pop e restituisce l'elemento superiore da outbox
Usando questo metodo, ogni elemento sarà in ogni pila esattamente una volta - il che significa che ogni elemento verrà spinto due volte e fatto scoppiare due volte, dando operazioni di tempo costante ammortizzate.
Ecco un'implementazione in 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();
}
}
Per capire come costruire una coda usando due pile, dovresti capire come invertire una pila in modo cristallino. Ricorda come funziona la pila, è molto simile alla pila di piatti della tua cucina. L'ultimo piatto lavato sarà in cima alla pila pulita, che in informatica è chiamata L ast I n F irst O ut (LIFO).
Immaginiamo il nostro stack come una bottiglia come di seguito;
Se spingiamo interi 1,2,3 rispettivamente, 3 sarà in cima allo stack. Poiché 1 verrà premuto per primo, quindi 2 verrà inserito in cima a 1. Infine, 3 verrà inserito in cima allo stack e l'ultimo stato del nostro stack rappresentato come una bottiglia sarà il seguente;
Ora abbiamo il nostro stack rappresentato come una bottiglia è popolata con valori 3,2,1. E vogliamo invertire la pila in modo che l'elemento superiore della pila sia 1 e l'elemento inferiore della pila sia 3. Cosa possiamo fare? Possiamo prendere la bottiglia e tenerla sottosopra in modo che tutti i valori siano invertiti in ordine?
Sì, possiamo farlo, ma quella è una bottiglia. Per fare lo stesso processo, dobbiamo disporre di un secondo stack che memorizzerà i primi elementi dello stack in ordine inverso. Mettiamo il nostro stack popolato a sinistra e il nostro nuovo stack vuoto a destra. Per invertire l'ordine degli elementi, faremo scoppiare ogni elemento dalla pila di sinistra e li sposteremo nella pila di destra. Puoi vedere cosa succede mentre lo facciamo nell'immagine qui sotto;
Quindi sappiamo come invertire uno stack.
Nella parte precedente, ho spiegato come possiamo invertire l'ordine degli elementi dello stack. Questo era importante, perché se spingiamo e pop gli elementi nello stack, l'output sarà esattamente nell'ordine inverso di una coda. Pensando a un esempio, spingiamo l'array di numeri interi {1, 2, 3, 4, 5}
in uno stack. Se pop gli elementi e li stampiamo fino a quando la pila non è vuota, avremo l'array nell'ordine inverso dell'ordine di push, che sarà {5, 4, 3, 2, 1}
Ricorda che per lo stesso input, se dequeue la coda fino a quando la coda è vuota, l'output sarà {1, 2, 3, 4, 5}
. Quindi è ovvio che per lo stesso ordine di input degli elementi, l'output della coda è esattamente il contrario dell'output di uno stack. Poiché sappiamo come invertire uno stack utilizzando uno stack aggiuntivo, possiamo costruire una coda utilizzando due stack.
Il nostro modello di coda sarà composto da due pile. Uno stack verrà utilizzato per l' enqueue
operazione (stack n. 1 a sinistra, verrà chiamato come stack di input), verrà utilizzato un altro stack per l' dequeue
operazione (stack n. 2 a destra, verrà chiamato come stack di output). Guarda l'immagine qui sotto;
Il nostro pseudo-codice è il seguente;
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
Accodiamo gli interi {1, 2, 3}
rispettivamente. I numeri interi verranno inseriti nello Stack di input ( Stack # 1 ) che si trova a sinistra;
Quindi cosa succederà se eseguiamo un'operazione di dequeue? Ogni volta che viene eseguita un'operazione di dequeue, la coda verifica se lo stack di output è vuoto o meno (vedere lo pseudo-codice sopra) Se lo stack di output è vuoto, lo stack di input verrà estratto sull'output in modo che gli elementi dello stack di input verrà invertito. Prima di restituire un valore, lo stato della coda sarà il seguente;
Controlla l'ordine degli elementi nello Stack di output (Stack # 2). È ovvio che possiamo estrarre gli elementi dallo Stack di output in modo che l'output sia lo stesso di se fossimo in coda di coda. Pertanto, se eseguiamo due operazioni di dequeue, prima otterremo {1, 2}
rispettivamente. Quindi l'elemento 3 sarà l'unico elemento dello Stack di output e lo Stack di input sarà vuoto. Se accodiamo gli elementi 4 e 5, lo stato della coda sarà il seguente;
Ora lo Stack di output non è vuoto e se eseguiamo un'operazione di dequeue, solo 3 verranno estratti dallo Stack di output. Quindi lo stato verrà visualizzato come di seguito;
Ancora una volta, se eseguiamo altre due operazioni di dequeue, sulla prima operazione di dequeue, la coda controllerà se lo Stack di output è vuoto, il che è vero. Quindi estrarre gli elementi dello stack di input e spingerli nello stack di output fino a quando lo stack di input è vuoto, quindi lo stato della coda sarà il seguente;
Facile da vedere, l'output delle due operazioni di dequeue sarà {4, 5}
Ecco un'implementazione in Java. Non userò l'implementazione esistente di Stack, quindi l'esempio qui reinventerà la ruota;
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
Puoi persino simulare una coda usando solo uno stack. Il secondo stack (temporaneo) può essere simulato dallo stack di chiamate ricorsive al metodo insert.
Il principio rimane lo stesso quando si inserisce un nuovo elemento nella coda:
Una classe di coda che utilizza un solo stack sarebbe la seguente:
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
nella coda usando la struttura dati sopra. la somma (1 + 2 + 4 + 8 + .... + 2(n-1))
risulta ~O(n^2)
. Spero che tu capisca il punto.
Le complessità temporali sarebbero comunque peggiori. Una buona implementazione della coda fa tutto in tempo costante.
modificare
Non sono sicuro del motivo per cui la mia risposta è stata sottoposta a downgrade. Se programmiamo, ci preoccupiamo della complessità del tempo e l'uso di due stack standard per creare una coda è inefficiente. È un punto molto valido e pertinente. Se qualcun altro sente la necessità di sottovalutare ulteriormente questo, sarei interessato a sapere perché.
Un po 'più di dettaglio : perché usare due pile è peggio di una semplice coda: se usi due pile e qualcuno chiama dequeue mentre la posta in uscita è vuota, hai bisogno di tempo lineare per arrivare in fondo alla posta in arrivo (come puoi vedere nel codice di Dave).
È possibile implementare una coda come un elenco collegato singolarmente (ogni elemento punta all'elemento inserito successivamente), mantenendo un puntatore aggiuntivo all'ultimo elemento inserito per i push (o rendendolo un elenco ciclico). L'implementazione di code e dequeue su questa struttura di dati è molto semplice da eseguire in tempo costante. È il tempo costante nel caso peggiore, non ammortizzato. E, poiché i commenti sembrano chiedere questo chiarimento, il tempo costante nel caso peggiore è strettamente migliore del tempo costante ammortizzato.
Lascia che la coda da implementare sia q e gli stack utilizzati per implementare q siano stack1 e stack2.
q può essere implementato in due modi:
Metodo 1 (Rendendo l'operazione enQueue costosa)
Questo metodo assicura che l'elemento appena inserito sia sempre all'inizio dello stack 1, in modo che l'operazione deQueue venga appena visualizzata dallo stack1. Per posizionare l'elemento in cima a stack1, viene utilizzato 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.
Metodo 2 (rendendo costosa l'operazione deQueue)
In questo metodo, nell'operazione in coda, il nuovo elemento viene inserito nella parte superiore dello stack1. Nel funzionamento in coda, se stack2 è vuoto, tutti gli elementi vengono spostati in stack2 e infine viene restituita la parte superiore di 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.
Il metodo 2 è decisamente migliore del metodo 1. Il metodo 1 sposta due volte tutti gli elementi nell'operazione enQueue, mentre il metodo 2 (nell'operazione deQueue) sposta gli elementi una volta e sposta gli elementi solo se stack2 vuoto.
Una soluzione in 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();
}
}
Due stack nella coda sono definiti come stack1 e stack2 .
Enqueue: gli elementi euqueued vengono sempre inseriti nello stack1
Dequeue: la parte superiore di stack2 può essere estratta poiché è il primo elemento inserito nella coda quando stack2 non è vuoto. Quando stack2 è vuoto, estraiamo tutti gli elementi dallo stack1 e li spingiamo nello stack2 uno per uno. Il primo elemento in una coda viene inserito nella parte inferiore dello stack1 . Può essere estratto direttamente dopo aver fatto scoppiare e spingere le operazioni poiché è in cima allo stack2 .
Quanto segue è lo stesso codice di esempio 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;
}
Questa soluzione è presa in prestito dal mio blog . Analisi più dettagliate con simulazioni operative dettagliate sono disponibili nella pagina Web del mio blog.
Dovrai estrarre tutto dal primo stack per ottenere l'elemento in basso. Quindi rimettili tutti sul secondo stack per ogni operazione di "dequeue".
per lo sviluppatore c # ecco il programma 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();
}
}
}
Implementare le seguenti operazioni di una coda usando stack.
push (x) - Spinge l'elemento x sul retro della coda.
pop () - Rimuove l'elemento davanti alla coda.
peek () - Ottieni l'elemento frontale.
empty () - Restituisce se la coda è vuota.
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();
}
}
Un'implementazione di una coda che utilizza due stack in 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()!)
}
}
}
}
Mentre otterrai molti post relativi all'implementazione di una coda con due stack: 1. O rendendo il processo enQueue molto più costoso 2. O rendendo il processo deQueue molto più costoso
https://www.geeksforgeeks.org/queue-using-stacks/
Un modo importante che ho scoperto dal precedente post era la costruzione di una coda con solo la struttura dei dati dello stack e lo stack delle chiamate di ricorsione.
Mentre si può sostenere che letteralmente si utilizzano ancora due stack, ma idealmente si utilizza solo una struttura di dati dello stack.
Di seguito è la spiegazione del problema:
Dichiarare uno stack singolo per enQueuing e deQueing dei dati e inviare i dati nello stack.
mentre deQueueing ha una condizione di base in cui l'elemento dello stack viene popolato quando la dimensione dello stack è 1. Questo assicurerà che non ci sia overflow dello stack durante la ricorsione di deQueue.
Mentre deQueueing prima pop i dati dalla parte superiore dello stack. Idealmente questo elemento sarà l'elemento presente nella parte superiore della pila. Ora, una volta fatto questo, chiama ricorsivamente la funzione deQueue e poi rimetti nello stack l'elemento spuntato sopra.
Il codice sarà simile al seguente:
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;
In questo modo è possibile creare una coda utilizzando una struttura dati a stack singolo e lo stack di chiamate di ricorsione.
Di seguito è la soluzione in linguaggio javascript che utilizza la sintassi 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 };
Di seguito è riportato l'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
. Quando vai di dequeue
nuovo, li sposterai in oggetti stack2
, mettendoli davanti a ciò che era già lì.
Risponderò a questa domanda in Go perché Go non ha molte raccolte nella sua libreria standard.
Dato che uno stack è davvero facile da implementare, ho pensato di provare a usare due stack per realizzare una coda a doppio attacco. Per capire meglio come sono arrivato alla mia risposta, ho diviso l'implementazione in due parti, la prima parte si spera sia più facile da capire ma è 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:]
}
}
Fondamentalmente sono due pile in cui permettiamo che il fondo delle pile sia manipolato l'uno dall'altro. Ho anche usato le convenzioni di denominazione STL, in cui le tradizionali operazioni push, pop, peek di uno stack hanno un prefisso fronte / retro sia che si riferiscano alla parte anteriore o posteriore della coda.
Il problema con il codice sopra è che non utilizza la memoria in modo molto efficiente. In realtà, cresce all'infinito fino a quando non si esaurisce lo spazio. È davvero brutto. La soluzione per questo è semplicemente riutilizzare il fondo dello spazio dello stack quando possibile. Dobbiamo introdurre un offset per tracciare questo dato che una porzione in Go non può crescere nella parte anteriore una volta ridotta.
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.")
}
}
}
Sono molte le piccole funzioni ma delle 6 funzioni 3 sono solo specchi dell'altra.
ecco la mia soluzione in java usando linklist.
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: in questo caso, l'operazione pop richiede molto tempo. Quindi non suggerirò di creare una coda usando due stack.
Con O(1)
dequeue()
, che è uguale alla risposta di 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()
(questo non è menzionato in questo post, quindi questa risposta), che utilizza anche il backtracking per eseguire il bubble up e restituire l'elemento più in basso.
// 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
Ovviamente, è un buon esercizio di codifica in quanto inefficiente ma elegante comunque.
** Soluzione JS facile **
/*
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();
}
}
Per ogni operazione di accodamento, aggiungiamo all'inizio dello stack1. Per ogni dequeue, svuotiamo il contenuto dello stack1 nello stack2 e rimuoviamo l'elemento in cima allo stack. La complessità temporale è O (n) per il dequeue, poiché dobbiamo copiare lo stack1 in stack2. la complessità temporale di accodamento è la stessa di uno stack normale
if (stack2 != null)
è sempre vero perché stack2
viene istanziato nel costruttore.
Implementazione della coda usando due oggetti 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()
e return inbox.size() + outbox.size()
, rispettivamente. Il codice di Dave L. genera già un'eccezione quando si esegue il dequeue da una coda vuota. La domanda originale non riguardava nemmeno Java; si trattava di strutture / algoritmi di dati in generale. L'implementazione Java era solo un'ulteriore illustrazione.