Stacks & Queues · 20 of 38
Implement Stack using Queue
Make a fair waiting line behave like a pile of plates, by sending older people to the back every time someone new arrives.
- Simple way
- time O(n) push, O(1) pop
room O(n) - Clever way
- time O(n) push, O(1) pop
room O(n)
The problem, simply
Build a stack, a pile where the last thing in is the first thing out. It needs push(x), pop(), top() and empty().
The catch: inside, you may only use queues. A queue is a fair waiting line. You can only add people at the back, look at or remove the person at the front, and ask for the size. The line gives back the oldest person first, but a stack must give back the newest. You have to flip that order somehow.
A story to remember it
Picture it
We pushed 1, then 2, then 3. A stack must give back 3 first, but a plain waiting line would give back 1 first.
- answer
- rejected
Examples
A normal run
- In
push 1, push 2, push 3, pop, top- Out
pop = 3, top = 2
3 was pushed last, so it comes out first. After that, 2 is on top.
Mixing pushes and pops
- In
push 1, pop, push 2, push 3, pop, pop, empty- Out
pop = 1, pop = 3, pop = 2, empty = true
Every pop gives back the newest number still inside. In the end nothing is left.
One item
- In
push 9, top, pop, empty- Out
top = 9, pop = 9, empty = true
With one item there is nothing to rotate. The front is the top.
Empty from the start
- In
empty- Out
true
A new stack is empty. Do not call pop() on it.
Way 1: the simple way
Use two lines: the main line q1 and a helper line q2. Keep q1 in newest first order at all times.
push(x): move everybody fromq1intoq2.- Put
xinto the now emptyq1. It is alone, so it is at the front. - Move everybody from
q2back intoq1, behindx. pop()andtop()use the front ofq1.
Pushing 3 with two lines: move 2 and 1 to the helper line, put 3 into the empty main line, then bring 2 and 1 back behind it.
- just added
- ignored
import java.util.*;
class MyStack {
private Queue<Integer> q1 = new ArrayDeque<>(); // main line, newest at the front
private Queue<Integer> q2 = new ArrayDeque<>(); // helper line
public void push(int x) {
while (!q1.isEmpty()) q2.offer(q1.poll()); // everybody steps aside
q1.offer(x); // the new kid stands first
while (!q2.isEmpty()) q1.offer(q2.poll()); // everybody comes back behind
}
public int pop() {
return q1.poll();
}
public int top() {
return q1.peek();
}
public boolean empty() {
return q1.isEmpty();
}
}How long it takes: each push moves all n items twice, so it is O(n). pop, top and empty are O(1). Extra room: the two lines together hold n items, O(n).
Way 2: the clever way
You do not need the helper line. One line can rotate itself, just like the polite lunch line.
push(x): remember the size beforexarrives. Call itk.- Add
xat the back. ktimes: take the front person and add them at the back. Nowxis at the front.pop(): remove the front.top(): peek at the front.
One waiting line, rotated after every push
01 / 09
Start with one empty waiting line. Our rule: the newest number must always stand at the front.
- looking now
- checking
- done
- answer
- just added
import java.util.*;
class MyStack {
private final Queue<Integer> q = new ArrayDeque<>();
public void push(int x) {
int k = q.size(); // how many were here before x
q.offer(x); // x joins at the back
for (int i = 0; i < k; i++) {
q.offer(q.poll()); // each older one walks to the back
}
}
public int pop() {
if (q.isEmpty()) throw new RuntimeException("stack is empty");
return q.poll(); // the front is the newest
}
public int top() {
if (q.isEmpty()) throw new RuntimeException("stack is empty");
return q.peek();
}
public boolean empty() {
return q.isEmpty();
}
}How long it takes: push rotates the n older items once, O(n). pop, top and empty are O(1). Extra room: one line of n items, O(n), and no helper line. Same speed as Way 1 but half the moving and only one queue.
Another way: cheap push, costly pop
You can also flip the deal. Let push be a plain offer, and do the rotating when someone asks for the top.
import java.util.*;
class MyStack {
private final Queue<Integer> q = new ArrayDeque<>();
public void push(int x) {
q.offer(x); // O(1)
}
public int pop() {
for (int i = 0; i < q.size() - 1; i++) {
q.offer(q.poll()); // bring the newest to the front
}
return q.poll();
}
public int top() {
int x = pop(); // find the newest
q.offer(x); // put it back at the back, where the newest lives
return x;
}
public boolean empty() {
return q.isEmpty();
}
}Here push is O(1) and pop and top are O(n). Pick this one when pushes happen much more often than pops.
Watch out for
- Calling
pop()ortop()on an empty stack.poll()returnsnull, which crashes when unboxed toint. - In the cheap push version,
top()must put the item back at the back of the line, because the back is where the newest item lives in that version. - Using
q.size()as the loop bound while the loop changes the queue. In the code above the size stays the same during a rotation, but read it once if in doubt.
What interviewers ask next
- Can both
pushandpopbeO(1)with only queues? No. A queue never changes the order of its items, so someone must pay to reverse the order. One of the two moves costsO(n). - Build a queue from two stacks, the reverse problem. Push into an "in" stack, and when the "out" stack is empty, pour everything across. Each item moves at most twice, so it is
O(1)on average. - Which version do you pick? If the program pushes a lot and rarely pops, use cheap push. If it reads the top a lot, use cheap pop, which is Way 2.
- What does
ArrayDequecost?offer,pollandpeekareO(1)on average, so the cost of each version comes only from the rotations.
Remember this
Grown-up words: the waiting line is a queue (first in, first out), the pile is a stack (last in, first out), joining at the back is offer, leaving the front is poll, and walking from front to back is a rotation.