Suponha que tenhamos duas pilhas e nenhuma outra variável temporária.
É possível "construir" uma estrutura de dados da fila usando apenas as duas pilhas?
algorithm
data-structures
stack
queue
Nitin
fonte
fonte
A - Como reverter uma pilha
Para entender como construir uma fila usando duas pilhas, você deve entender como reverter uma pilha cristalina. Lembre-se de como a pilha funciona, é muito semelhante à pilha de pratos da sua cozinha. O último prato lavado estará no topo da pilha limpo, o que é chamado como L ast eu n F irst O ut (LIFO) em informática.
Vamos imaginar nossa pilha como uma garrafa como abaixo;
Se pressionarmos os números inteiros 1,2,3, respectivamente, então 3 estará no topo da pilha. Como 1 será pressionado primeiro, depois 2 serão colocados no topo de 1. Por fim, 3 serão colocados no topo da pilha e o estado mais recente da nossa pilha representado como uma garrafa será como abaixo;
Agora temos nossa pilha representada como uma garrafa é preenchida com valores 3,2,1. E queremos inverter a pilha para que o elemento superior da pilha seja 1 e o elemento inferior da pilha seja 3. O que podemos fazer? Podemos pegar a garrafa e segurá-la de cabeça para baixo, para que todos os valores sejam revertidos em ordem?
Sim, podemos fazer isso, mas isso é uma garrafa. Para fazer o mesmo processo, precisamos ter uma segunda pilha que armazene os primeiros elementos da pilha na ordem inversa. Vamos colocar nossa pilha preenchida à esquerda e nossa nova pilha vazia à direita. Para inverter a ordem dos elementos, vamos exibir cada elemento da pilha esquerda e empurrá-los para a pilha direita. Você pode ver o que acontece como fazemos na imagem abaixo;
Então, sabemos como reverter uma pilha.
B - Usando duas pilhas como uma fila
Na parte anterior, expliquei como podemos reverter a ordem dos elementos da pilha. Isso foi importante, porque se empurrarmos e colocarmos elementos na pilha, a saída será exatamente na ordem inversa de uma fila. Pensando em um exemplo, vamos enviar a matriz de números inteiros
{1, 2, 3, 4, 5}
para uma pilha. Se popularmos os elementos e os imprimirmos até que a pilha esteja vazia, obteremos a matriz na ordem inversa da ordem de envio, que será{5, 4, 3, 2, 1}
lembre-se de que, para a mesma entrada, se retirarmos a fila da fila até que a fila esteja vazia, a saída será{1, 2, 3, 4, 5}
. Portanto, é óbvio que, para a mesma ordem de entrada de elementos, a saída da fila é exatamente o inverso da saída de uma pilha. Como sabemos como reverter uma pilha usando uma pilha extra, podemos construir uma fila usando duas pilhas.Nosso modelo de fila consistirá em duas pilhas. Uma pilha será usada para
enqueue
operação (a pilha nº 1, à esquerda, será chamada como Pilha de Entrada), outra pilha será usada para adequeue
operação (a pilha nº 2, à direita, será chamada como Pilha de Saída). Veja a imagem abaixo;Nosso pseudo-código é como abaixo;
Operação de enfileiramento
Operação de desenfileiramento
Vamos enfileirar os números inteiros,
{1, 2, 3}
respectivamente. Os números inteiros serão pressionados na pilha de entrada ( pilha nº 1 ), localizada à esquerda;Então, o que acontecerá se executarmos uma operação de desenfileiramento? Sempre que uma operação de desenfileiramento é executada, a fila verifica se a pilha de saída está vazia ou não (consulte o pseudocódigo acima) Se a pilha de saída estiver vazia, a pilha de entrada será extraída na saída para que os elementos Pilha de entrada será revertida. Antes de retornar um valor, o estado da fila será o seguinte;
Confira a ordem dos elementos na pilha de saída (pilha 2). É óbvio que podemos exibir os elementos da pilha de saída para que a saída seja a mesma que se desenfileirássemos de uma fila. Assim, se executarmos duas operações de desenfileiramento, primeiro obteremos
{1, 2}
respectivamente. O elemento 3 será o único elemento da pilha de saída e a pilha de entrada ficará vazia. Se enfileirarmos os elementos 4 e 5, o estado da fila será o seguinte;Agora a pilha de saída não está vazia e, se executarmos uma operação de desenfileiramento, apenas 3 serão salvas da pilha de saída. Então o estado será visto como abaixo;
Novamente, se executarmos mais duas operações de desenfileiramento, na primeira operação de desenfileiramento, a fila verificará se a pilha de saída está vazia, o que é verdadeiro. Em seguida, retire os elementos da pilha de entrada e empurre-os para a pilha de saída até que a pilha de entrada esteja vazia; o estado da fila será o seguinte;
Fácil de ver, a saída das duas operações de desenfileiramento será
{4, 5}
C - Implementação da fila construída com duas pilhas
Aqui está uma implementação em Java. Não vou usar a implementação existente do Stack, por isso o exemplo aqui vai reinventar a roda;
C - 1) Classe MyStack: Uma Implementação Simples de Pilha
C - 2) Classe MyQueue: Implementação de Filas Usando Duas Pilhas
C - 3) Código Demo
C - 4) Saída de amostra
fonte
Você pode até simular uma fila usando apenas uma pilha. A segunda pilha (temporária) pode ser simulada pela pilha de chamadas recursivas para o método de inserção.
O princípio permanece o mesmo ao inserir um novo elemento na fila:
Uma classe Queue usando apenas uma pilha seria a seguinte:
fonte
n items
na fila usando a estrutura de dados acima. a soma(1 + 2 + 4 + 8 + .... + 2(n-1))
resulta em~O(n^2)
. Espero que voce tenha entendido.As complexidades do tempo seriam piores, no entanto. Uma boa implementação de fila faz tudo em tempo constante.
Editar
Não sei por que minha resposta foi votada aqui. Se programarmos, nos preocupamos com a complexidade do tempo, e o uso de duas pilhas padrão para criar uma fila é ineficiente. É um ponto muito válido e relevante. Se alguém sentir mais a necessidade de diminuir o voto, eu estaria interessado em saber o porquê.
Um pouco mais de detalhe : por que usar duas pilhas é pior do que apenas uma fila: se você usa duas pilhas e alguém chama de desenfileirar enquanto a caixa de saída está vazia, você precisa de tempo linear para chegar ao fundo da caixa de entrada (como você pode ver no código de Dave).
Você pode implementar uma fila como uma lista vinculada individualmente (cada elemento aponta para o próximo elemento inserido), mantendo um ponteiro extra para o último elemento inserido para envio (ou transformando-o em uma lista cíclica). Implementar fila e desenfileirar nessa estrutura de dados é muito fácil de executar em tempo constante. Esse é o pior caso, tempo constante, não amortizado. E, como os comentários parecem pedir esse esclarecimento, o tempo constante do pior caso é estritamente melhor que o tempo constante amortizado.
fonte
Seja q a fila a ser implementada e as pilhas usadas para implementar q sejam pilha1 e pilha2.
q pode ser implementado de duas maneiras:
Método 1 (tornando a operação enQueue cara)
Este método garante que o elemento recém-inserido esteja sempre no topo da pilha 1, para que a operação deQueue saia da pilha1. Para colocar o elemento no topo da pilha1, a pilha2 é usada.
Método 2 (tornando a operação deQueue cara)
Nesse método, na operação em fila, o novo elemento é inserido na parte superior da pilha1. Na operação de remoção da fila, se a pilha2 estiver vazia, todos os elementos serão movidos para a pilha2 e, finalmente, o topo da pilha2 será retornado.
O método 2 é definitivamente melhor que o método 1. O método 1 move todos os elementos duas vezes na operação enQueue, enquanto o método 2 (na operação deQueue) move os elementos uma vez e move os elementos apenas se a pilha2 estiver vazia.
fonte
Uma solução em c #
fonte
Duas pilhas na fila são definidas como pilha1 e pilha2 .
Enfileirar: os elementos em fila são sempre empurrados para a pilha1
Retirar da fila: A parte superior da pilha2 pode ser removida , pois é o primeiro elemento inserido na fila quando a pilha2 não está vazia. Quando a pilha2 está vazia, exibimos todos os elementos da pilha1 e os empurramos para a pilha2, um por um. O primeiro elemento de uma fila é colocado na parte inferior da pilha1 . Ele pode ser destacado diretamente após operações de popping e push, uma vez que está no topo da pilha2 .
A seguir está o mesmo código de exemplo C ++:
Esta solução é emprestada do meu blog . Uma análise mais detalhada com simulações de operação passo a passo está disponível na página do meu blog.
fonte
Você precisará extrair tudo da primeira pilha para obter o elemento inferior. Em seguida, coloque-os de volta na segunda pilha para cada operação de "desenfileirar".
fonte
para desenvolvedor de c #, aqui está o programa completo:
fonte
Implemente as seguintes operações de uma fila usando pilhas.
push (x) - empurre o elemento x para o fundo da fila.
pop () - Remove o elemento da frente da fila.
peek () - Pega o elemento da frente.
empty () - Retorna se a fila está vazia.
fonte
fonte
Uma implementação de uma fila usando duas pilhas no Swift:
fonte
Embora você receba muitas postagens relacionadas à implementação de uma fila com duas pilhas: 1. Tornando o processo enQueue muito mais caro 2. Ou tornando o processo deQueue muito mais caro
https://www.geeksforgeeks.org/queue-using-stacks/
Uma maneira importante que descobri no post acima foi construir a fila com apenas a estrutura de dados da pilha e a pilha de chamadas de recursão.
Embora se possa argumentar que literalmente isso ainda está usando duas pilhas, mas, idealmente, isso está usando apenas uma estrutura de dados da pilha.
Abaixo está a explicação do problema:
Declare uma única pilha para enQueuing e deQueing os dados e envie-os para a pilha.
enquanto o deQueueing possui uma condição básica em que o elemento da pilha é exibido quando o tamanho da pilha é 1. Isso garantirá que não haja excesso de pilha durante a recursão do deQueue.
Enquanto o DeQueueing, primeiro pop, os dados do topo da pilha. Idealmente, esse elemento será o elemento que está presente no topo da pilha. Agora que isso for feito, chame recursivamente a função deQueue e empurre o elemento exibido acima de volta para a pilha.
O código será semelhante abaixo:
Dessa forma, você pode criar uma fila usando uma única estrutura de dados da pilha e a pilha de chamadas de recursão.
fonte
Abaixo está a solução na linguagem javascript usando a sintaxe ES6.
Stack.js
QueueUsingTwoStacks.js
Abaixo está o uso:
index.js
fonte
stack1
. Quando você voltardequeue
, mova os itens parastack2
eles, colocando-os à frente do que já estava lá.Responderei a essa pergunta no Go porque o Go não possui muitas coleções em sua biblioteca padrão.
Como uma pilha é realmente fácil de implementar, pensei em tentar usar duas pilhas para realizar uma fila dupla. Para entender melhor como cheguei à minha resposta, dividi a implementação em duas partes, espero que a primeira parte seja mais fácil de entender, mas está incompleta.
São basicamente duas pilhas onde nós permitimos que o fundo das pilhas seja manipulado um pelo outro. Também usei as convenções de nomenclatura STL, nas quais as operações tradicionais push, pop, peek de uma pilha têm um prefixo de frente / verso, quer se refiram à frente ou atrás da fila.
O problema com o código acima é que ele não usa a memória com muita eficiência. Na verdade, cresce infinitamente até você ficar sem espaço. Isso é muito ruim. A correção para isso é simplesmente reutilizar a parte inferior do espaço da pilha sempre que possível. Temos que introduzir um deslocamento para rastrear isso, pois uma fatia no Go não pode crescer na frente depois que ela encolher.
São muitas funções pequenas, mas das 6 funções, 3 delas são apenas espelhos da outra.
fonte
Aqui está a minha solução em java usando o linklist.
}
Nota: Nesse caso, a operação pop consome muito tempo. Portanto, não vou sugerir a criação de uma fila usando duas pilhas.
fonte
With
O(1)
dequeue()
, que é o mesmo que a resposta do pythonquick :Com
O(1)
enqueue()
(isso não é mencionado neste post, portanto, esta resposta), que também usa o retorno para fazer bolhas e retornar o item mais baixo.Obviamente, é um bom exercício de codificação, pois é ineficiente, mas elegante.
fonte
** Solução JS fácil **
fonte
Para cada operação de enfileiramento, adicionamos ao topo da pilha1. Para cada desenfileiramento, esvaziamos o conteúdo da pilha1 na pilha2 e removemos o elemento na parte superior da pilha. A complexidade de tempo é O (n) para desenfileirar, pois precisamos copiar a pilha1 para a pilha2. A complexidade temporal do enfileiramento é igual a uma pilha comum
fonte
if (stack2 != null)
sempre é verdadeiro porquestack2
é instanciado no construtor.Implementação de fila usando dois objetos java.util.Stack:
fonte
return inbox.isEmpty() && outbox.isEmpty()
ereturn inbox.size() + outbox.size()
, respectivamente. O código de Dave L. já lança uma exceção quando você desenfileirar de uma fila vazia. A pergunta original não era nem sobre Java; tratava-se de estruturas / algoritmos de dados em geral. A implementação do Java foi apenas uma ilustração adicional.