Suppose we have two stacks and no other temporary variable.
Is to possible to "construct" a queue data structure using only the two stacks?
Suppose we have two stacks and no other temporary variable.
Is to possible to "construct" a queue data structure using only the two stacks?
You can even simulate a queue using only one stack. The second (temporary) stack can be simulated by the call stack of recursive calls to the insert method.
The principle stays the same when inserting a new element into the queue:
A Queue class using only one Stack, would be as follows:
I'll answer this question in Go because Go does not have a rich a lot of collections in its standard library.
Since a stack is really easy to implement I thought I'd try and use two stacks to accomplish a double ended queue. To better understand how I arrived at my answer I've split the implementation in two parts, the first part is hopefully easier to understand but it's incomplete.
It's basically two stacks where we allow the bottom of the stacks to be manipulated by each other. I've also used the STL naming conventions, where the traditional push, pop, peek operations of a stack have a front/back prefix whether they refer to the front or back of the queue.
The issue with the above code is that it doesn't use memory very efficiently. Actually, it grows endlessly until you run out of space. That's really bad. The fix for this is to simply reuse the bottom of the stack space whenever possible. We have to introduce an offset to track this since a slice in Go cannot grow in the front once shrunk.
It's a lot of small functions but of the 6 functions 3 of them are just mirrors of the other.
While you will get a lot of posts related to implementing a queue with two stacks : 1. Either by making the enQueue process a lot more costly 2. Or by making the deQueue process a lot more costly
https://www.geeksforgeeks.org/queue-using-stacks/
One important way I found out from the above post was constructing queue with only stack data structure and the recursion call stack.
While one can argue that literally this is still using two stacks, but then ideally this is using only one stack data structure.
Below is the explanation of the problem:
Declare a single stack for enQueuing and deQueing the data and push the data into the stack.
while deQueueing have a base condition where the element of the stack is poped when the size of the stack is 1. This will ensure that there is no stack overflow during the deQueue recursion.
While deQueueing first pop the data from the top of the stack. Ideally this element will be the element which is present at the top of the stack. Now once this is done, recursively call the deQueue function and then push the element popped above back into the stack.
The code will look like below:
This way you can create a queue using a single stack data structure and the recursion call stack.
The time complexities would be worse, though. A good queue implementation does everything in constant time.
Edit
Not sure why my answer has been downvoted here. If we program, we care about time complexity, and using two standard stacks to make a queue is inefficient. It's a very valid and relevant point. If someone else feels the need to downvote this more, I would be interested to know why.
A little more detail: on why using two stacks is worse than just a queue: if you use two stacks, and someone calls dequeue while the outbox is empty, you need linear time to get to the bottom of the inbox (as you can see in Dave's code).
You can implement a queue as a singly-linked list (each element points to the next-inserted element), keeping an extra pointer to the last-inserted element for pushes (or making it a cyclic list). Implementing queue and dequeue on this data structure is very easy to do in constant time. That's worst-case constant time, not amortized. And, as the comments seem to ask for this clarification, worst-case constant time is strictly better than amortized constant time.
Let queue to be implemented be q and stacks used to implement q be stack1 and stack2.
q can be implemented in two ways:
Method 1 (By making enQueue operation costly)
This method makes sure that newly entered element is always at the top of stack 1, so that deQueue operation just pops from stack1. To put the element at top of stack1, stack2 is used.
Method 2 (By making deQueue operation costly)
In this method, in en-queue operation, the new element is entered at the top of stack1. In de-queue operation, if stack2 is empty then all the elements are moved to stack2 and finally top of stack2 is returned.
Method 2 is definitely better than method 1. Method 1 moves all the elements twice in enQueue operation, while method 2 (in deQueue operation) moves the elements once and moves elements only if stack2 empty.
For every enqueue operation, we add to the top of the stack1. For every dequeue, we empty the content's of stack1 into stack2, and remove the element at top of the stack.Time complexity is O(n) for dequeue, as we have to copy the stack1 to stack2. time complexity of enqueue is the same as a regular stack