Skip to content

Commit 299e8d5

Browse files
authored
Merge pull request #87 from jaebradley/queue-two-stacks
Queue two stacks
2 parents d0662d5 + d652bbf commit 299e8d5

File tree

3 files changed

+232
-0
lines changed

3 files changed

+232
-0
lines changed

codereview/twoStackQueue.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
## Purpose
2+
I recently came across this interview question
3+
> Implement a queue with 2 `Stack`s. Your queue should have an `enqueue` and a `dequeue` function.
4+
Assume you already have a `Stack` implementation
5+
6+
## Discussion
7+
The way I thought about this problem is that one `Stack` would keep track of the input elements (i.e. what's been enqueued)
8+
and at some point, this `Stack` would transfer its elements to the second `Stack`.
9+
10+
By `pop`ping the results of the first `Stack` onto the the second `Stack`, the second `Stack` now represents the elements, ordered
11+
by when they were first inputted into the queue.
12+
13+
By `pop`ping off this second `Stack`, elements are now dequeued in the order that they were enqueued.
14+
15+
What happens in the case where some elements are enqueued, an element is dequeued, and then a bunch of elements are enqueued?
16+
When does the transferring of elements from the first `Stack` to the second take place?
17+
18+
In order to preserve the order of elements, the transfer of elements doesn't take place until the second `Stack` is empty.
19+
Thus, when dequeueing, first check that the second `Stack` is empty. If it isn't, then transfer the elements from the first `Stack`
20+
to the second.
21+
22+
For example, take the case where `1`, `2`, and `3` are enqueued.
23+
1. The first `Stack` looks like this: `| 1 | 2 | 3|`.
24+
2. The second `Stack` is empty.
25+
3. When `dequeue` is called, `3`, `2`, and `1` are popped off the first `Stack` in that order.
26+
4. Now the first `Stack` is empty
27+
5. The second `Stack` looks like `| 3 | 2 | 1 |`.
28+
6. Since `dequeue` was called, we `pop` the second `Stack` (bye bye `1`) and now it looks like `| 3 | 2 |`.
29+
7. Now suppose `4` is enqueued.
30+
8. The first `Stack` is no longer empty - it looks like: `| 4 |`.
31+
9. Now suppose `dequeue` is called again - we can't immediately transfer `4` from the first `Stack` to the second since
32+
the second `Stack` would look like `| 3 | 2 | 4 |`.
33+
10. Thus, we have to wait until all elements have been popped off the second `Stack` to transfer the elements from the first `Stack`.
34+
35+
36+
## Implementation
37+
38+
<!-- language: lang-java --!>
39+
40+
import java.util.NoSuchElementException;
41+
import java.util.Stack;
42+
43+
public class TwoStackQueue<T> {
44+
private final Stack<T> input = new Stack<>();
45+
private final Stack<T> output = new Stack<>();
46+
47+
public void enqueue(T data) {
48+
input.add(data);
49+
}
50+
51+
public T dequeue() {
52+
if (isEmpty()) {
53+
throw new NoSuchElementException("Unable to dequeue on empty queue");
54+
}
55+
56+
transfer();
57+
58+
return output.pop();
59+
}
60+
61+
public boolean isEmpty() {
62+
return input.empty() && output.empty();
63+
}
64+
65+
public T peek() {
66+
if (isEmpty()) {
67+
throw new NoSuchElementException("Unable to peek on empty queue");
68+
}
69+
70+
transfer();
71+
72+
return output.peek();
73+
}
74+
75+
private void transfer() {
76+
if (output.empty()) {
77+
while (!input.empty()) {
78+
output.push(input.pop());
79+
}
80+
}
81+
}
82+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package problems.impl;
2+
3+
import java.util.NoSuchElementException;
4+
import java.util.Stack;
5+
6+
public class TwoStackQueue<T> {
7+
private final Stack<T> input = new Stack<>();
8+
private final Stack<T> output = new Stack<>();
9+
10+
public void enqueue(T data) {
11+
input.add(data);
12+
}
13+
14+
public T dequeue() {
15+
if (isEmpty()) {
16+
throw new NoSuchElementException("Unable to dequeue on empty queue");
17+
}
18+
19+
transfer();
20+
21+
return output.pop();
22+
}
23+
24+
public boolean isEmpty() {
25+
return input.empty() && output.empty();
26+
}
27+
28+
public T peek() {
29+
if (isEmpty()) {
30+
throw new NoSuchElementException("Unable to peek on empty queue");
31+
}
32+
33+
transfer();
34+
35+
return output.peek();
36+
}
37+
38+
private void transfer() {
39+
if (output.empty()) {
40+
while (!input.empty()) {
41+
output.push(input.pop());
42+
}
43+
}
44+
}
45+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package problems.impl;
2+
3+
import org.junit.Test;
4+
5+
import java.util.NoSuchElementException;
6+
7+
import static org.junit.Assert.*;
8+
9+
public class TwoStackQueueTest {
10+
11+
private final String firstValue = "firstValue";
12+
private final String secondValue = "secondValue";
13+
private final String thirdValue = "thirdValue";
14+
15+
@Test
16+
public void itShouldEnqueueElement() {
17+
TwoStackQueue<String> queue = new TwoStackQueue<>();
18+
queue.enqueue(firstValue);
19+
assertFalse(queue.isEmpty());
20+
}
21+
22+
@Test
23+
public void itShouldEnqueueAndDequeueElements() {
24+
TwoStackQueue<String> queue = new TwoStackQueue<>();
25+
queue.enqueue(firstValue);
26+
queue.enqueue(secondValue);
27+
assertEquals(firstValue, queue.dequeue());
28+
assertEquals(secondValue, queue.dequeue());
29+
}
30+
31+
@Test
32+
public void itShouldEnqueueAndDequeueElementsTwice() {
33+
TwoStackQueue<String> queue = new TwoStackQueue<>();
34+
queue.enqueue(firstValue);
35+
queue.enqueue(secondValue);
36+
assertEquals(firstValue, queue.dequeue());
37+
38+
queue.enqueue(thirdValue);
39+
assertEquals(secondValue, queue.dequeue());
40+
assertEquals(thirdValue, queue.dequeue());
41+
}
42+
43+
@Test
44+
public void itShouldThrowWhenDequeueingEmptyQueue() {
45+
TwoStackQueue<String> queue = new TwoStackQueue<>();
46+
try {
47+
queue.dequeue();
48+
} catch (NoSuchElementException e) {
49+
// expected
50+
}
51+
}
52+
53+
@Test
54+
public void itShouldEnqueueAndPeekElements() {
55+
TwoStackQueue<String> queue = new TwoStackQueue<>();
56+
queue.enqueue(firstValue);
57+
queue.enqueue(secondValue);
58+
assertEquals(firstValue, queue.peek());
59+
60+
queue.dequeue();
61+
assertEquals(secondValue, queue.peek());
62+
}
63+
64+
@Test
65+
public void itShouldEnqueueAndPeekElementsTwice() {
66+
TwoStackQueue<String> queue = new TwoStackQueue<>();
67+
queue.enqueue(firstValue);
68+
queue.enqueue(secondValue);
69+
assertEquals(firstValue, queue.peek());
70+
71+
queue.dequeue();
72+
73+
queue.enqueue(thirdValue);
74+
assertEquals(secondValue, queue.peek());
75+
76+
queue.dequeue();
77+
assertEquals(thirdValue, queue.peek());
78+
}
79+
80+
@Test
81+
public void itShouldThrowWhenPeekingEmptyQueue() {
82+
TwoStackQueue<String> queue = new TwoStackQueue<>();
83+
try {
84+
queue.peek();
85+
} catch (NoSuchElementException e) {
86+
// expected
87+
}
88+
}
89+
90+
@Test
91+
public void itShouldBeEmptyForInstantiatedQueue() {
92+
TwoStackQueue<String> queue = new TwoStackQueue<>();
93+
assertTrue(queue.isEmpty());
94+
}
95+
96+
@Test
97+
public void itShouldBeEmptyAfterEnqueueingAndDequeueing() {
98+
TwoStackQueue<String> queue = new TwoStackQueue<>();
99+
queue.enqueue(firstValue);
100+
assertFalse(queue.isEmpty());
101+
102+
queue.dequeue();
103+
assertTrue(queue.isEmpty());
104+
}
105+
}

0 commit comments

Comments
 (0)