Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions codereview/twoStackQueue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
## Purpose
I recently came across this interview question
> Implement a queue with 2 `Stack`s. Your queue should have an `enqueue` and a `dequeue` function.
Assume you already have a `Stack` implementation

## Discussion
The way I thought about this problem is that one `Stack` would keep track of the input elements (i.e. what's been enqueued)
and at some point, this `Stack` would transfer its elements to the second `Stack`.

By `pop`ping the results of the first `Stack` onto the the second `Stack`, the second `Stack` now represents the elements, ordered
by when they were first inputted into the queue.

By `pop`ping off this second `Stack`, elements are now dequeued in the order that they were enqueued.

What happens in the case where some elements are enqueued, an element is dequeued, and then a bunch of elements are enqueued?
When does the transferring of elements from the first `Stack` to the second take place?

In order to preserve the order of elements, the transfer of elements doesn't take place until the second `Stack` is empty.
Thus, when dequeueing, first check that the second `Stack` is empty. If it isn't, then transfer the elements from the first `Stack`
to the second.

For example, take the case where `1`, `2`, and `3` are enqueued.
1. The first `Stack` looks like this: `| 1 | 2 | 3|`.
2. The second `Stack` is empty.
3. When `dequeue` is called, `3`, `2`, and `1` are popped off the first `Stack` in that order.
4. Now the first `Stack` is empty
5. The second `Stack` looks like `| 3 | 2 | 1 |`.
6. Since `dequeue` was called, we `pop` the second `Stack` (bye bye `1`) and now it looks like `| 3 | 2 |`.
7. Now suppose `4` is enqueued.
8. The first `Stack` is no longer empty - it looks like: `| 4 |`.
9. Now suppose `dequeue` is called again - we can't immediately transfer `4` from the first `Stack` to the second since
the second `Stack` would look like `| 3 | 2 | 4 |`.
10. Thus, we have to wait until all elements have been popped off the second `Stack` to transfer the elements from the first `Stack`.


## Implementation

<!-- language: lang-java --!>

import java.util.NoSuchElementException;
import java.util.Stack;

public class TwoStackQueue<T> {
private final Stack<T> input = new Stack<>();
private final Stack<T> output = new Stack<>();

public void enqueue(T data) {
input.add(data);
}

public T dequeue() {
if (isEmpty()) {
throw new NoSuchElementException("Unable to dequeue on empty queue");
}

transfer();

return output.pop();
}

public boolean isEmpty() {
return input.empty() && output.empty();
}

public T peek() {
if (isEmpty()) {
throw new NoSuchElementException("Unable to peek on empty queue");
}

transfer();

return output.peek();
}

private void transfer() {
if (output.empty()) {
while (!input.empty()) {
output.push(input.pop());
}
}
}
}
45 changes: 45 additions & 0 deletions src/main/java/problems/impl/TwoStackQueue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package problems.impl;

import java.util.NoSuchElementException;
import java.util.Stack;

public class TwoStackQueue<T> {
private final Stack<T> input = new Stack<>();
private final Stack<T> output = new Stack<>();

public void enqueue(T data) {
input.add(data);
}

public T dequeue() {
if (isEmpty()) {
throw new NoSuchElementException("Unable to dequeue on empty queue");
}

transfer();

return output.pop();
}

public boolean isEmpty() {
return input.empty() && output.empty();
}

public T peek() {
if (isEmpty()) {
throw new NoSuchElementException("Unable to peek on empty queue");
}

transfer();

return output.peek();
}

private void transfer() {
if (output.empty()) {
while (!input.empty()) {
output.push(input.pop());
}
}
}
}
105 changes: 105 additions & 0 deletions src/test/java/problems/impl/TwoStackQueueTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package problems.impl;

import org.junit.Test;

import java.util.NoSuchElementException;

import static org.junit.Assert.*;

public class TwoStackQueueTest {

private final String firstValue = "firstValue";
private final String secondValue = "secondValue";
private final String thirdValue = "thirdValue";

@Test
public void itShouldEnqueueElement() {
TwoStackQueue<String> queue = new TwoStackQueue<>();
queue.enqueue(firstValue);
assertFalse(queue.isEmpty());
}

@Test
public void itShouldEnqueueAndDequeueElements() {
TwoStackQueue<String> queue = new TwoStackQueue<>();
queue.enqueue(firstValue);
queue.enqueue(secondValue);
assertEquals(firstValue, queue.dequeue());
assertEquals(secondValue, queue.dequeue());
}

@Test
public void itShouldEnqueueAndDequeueElementsTwice() {
TwoStackQueue<String> queue = new TwoStackQueue<>();
queue.enqueue(firstValue);
queue.enqueue(secondValue);
assertEquals(firstValue, queue.dequeue());

queue.enqueue(thirdValue);
assertEquals(secondValue, queue.dequeue());
assertEquals(thirdValue, queue.dequeue());
}

@Test
public void itShouldThrowWhenDequeueingEmptyQueue() {
TwoStackQueue<String> queue = new TwoStackQueue<>();
try {
queue.dequeue();
} catch (NoSuchElementException e) {
// expected
}
}

@Test
public void itShouldEnqueueAndPeekElements() {
TwoStackQueue<String> queue = new TwoStackQueue<>();
queue.enqueue(firstValue);
queue.enqueue(secondValue);
assertEquals(firstValue, queue.peek());

queue.dequeue();
assertEquals(secondValue, queue.peek());
}

@Test
public void itShouldEnqueueAndPeekElementsTwice() {
TwoStackQueue<String> queue = new TwoStackQueue<>();
queue.enqueue(firstValue);
queue.enqueue(secondValue);
assertEquals(firstValue, queue.peek());

queue.dequeue();

queue.enqueue(thirdValue);
assertEquals(secondValue, queue.peek());

queue.dequeue();
assertEquals(thirdValue, queue.peek());
}

@Test
public void itShouldThrowWhenPeekingEmptyQueue() {
TwoStackQueue<String> queue = new TwoStackQueue<>();
try {
queue.peek();
} catch (NoSuchElementException e) {
// expected
}
}

@Test
public void itShouldBeEmptyForInstantiatedQueue() {
TwoStackQueue<String> queue = new TwoStackQueue<>();
assertTrue(queue.isEmpty());
}

@Test
public void itShouldBeEmptyAfterEnqueueingAndDequeueing() {
TwoStackQueue<String> queue = new TwoStackQueue<>();
queue.enqueue(firstValue);
assertFalse(queue.isEmpty());

queue.dequeue();
assertTrue(queue.isEmpty());
}
}