From 3c676fc1bf2b3c32ab7660c99fa27cb6da9a2719 Mon Sep 17 00:00:00 2001 From: jaebradley Date: Thu, 3 Aug 2017 08:32:25 -0400 Subject: [PATCH 1/2] initial implementation --- .../java/problems/impl/TwoStackQueue.java | 45 ++++++++ .../java/problems/impl/TwoStackQueueTest.java | 105 ++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 src/main/java/problems/impl/TwoStackQueue.java create mode 100644 src/test/java/problems/impl/TwoStackQueueTest.java diff --git a/src/main/java/problems/impl/TwoStackQueue.java b/src/main/java/problems/impl/TwoStackQueue.java new file mode 100644 index 0000000..77f8e99 --- /dev/null +++ b/src/main/java/problems/impl/TwoStackQueue.java @@ -0,0 +1,45 @@ +package problems.impl; + +import java.util.NoSuchElementException; +import java.util.Stack; + +public class TwoStackQueue { + private final Stack input = new Stack<>(); + private final Stack 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()); + } + } + } +} diff --git a/src/test/java/problems/impl/TwoStackQueueTest.java b/src/test/java/problems/impl/TwoStackQueueTest.java new file mode 100644 index 0000000..c1881bb --- /dev/null +++ b/src/test/java/problems/impl/TwoStackQueueTest.java @@ -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 queue = new TwoStackQueue<>(); + queue.enqueue(firstValue); + assertFalse(queue.isEmpty()); + } + + @Test + public void itShouldEnqueueAndDequeueElements() { + TwoStackQueue queue = new TwoStackQueue<>(); + queue.enqueue(firstValue); + queue.enqueue(secondValue); + assertEquals(firstValue, queue.dequeue()); + assertEquals(secondValue, queue.dequeue()); + } + + @Test + public void itShouldEnqueueAndDequeueElementsTwice() { + TwoStackQueue 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 queue = new TwoStackQueue<>(); + try { + queue.dequeue(); + } catch (NoSuchElementException e) { + // expected + } + } + + @Test + public void itShouldEnqueueAndPeekElements() { + TwoStackQueue queue = new TwoStackQueue<>(); + queue.enqueue(firstValue); + queue.enqueue(secondValue); + assertEquals(firstValue, queue.peek()); + + queue.dequeue(); + assertEquals(secondValue, queue.peek()); + } + + @Test + public void itShouldEnqueueAndPeekElementsTwice() { + TwoStackQueue 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 queue = new TwoStackQueue<>(); + try { + queue.peek(); + } catch (NoSuchElementException e) { + // expected + } + } + + @Test + public void itShouldBeEmptyForInstantiatedQueue() { + TwoStackQueue queue = new TwoStackQueue<>(); + assertTrue(queue.isEmpty()); + } + + @Test + public void itShouldBeEmptyAfterEnqueueingAndDequeueing() { + TwoStackQueue queue = new TwoStackQueue<>(); + queue.enqueue(firstValue); + assertFalse(queue.isEmpty()); + + queue.dequeue(); + assertTrue(queue.isEmpty()); + } +} \ No newline at end of file From d652bbf285c7b2927e36cca688b8ad8d77ba52cf Mon Sep 17 00:00:00 2001 From: jaebradley Date: Thu, 3 Aug 2017 08:50:32 -0400 Subject: [PATCH 2/2] add markdown --- codereview/twoStackQueue.md | 82 +++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 codereview/twoStackQueue.md diff --git a/codereview/twoStackQueue.md b/codereview/twoStackQueue.md new file mode 100644 index 0000000..06267a5 --- /dev/null +++ b/codereview/twoStackQueue.md @@ -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 + + + + import java.util.NoSuchElementException; + import java.util.Stack; + + public class TwoStackQueue { + private final Stack input = new Stack<>(); + private final Stack 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()); + } + } + } + } \ No newline at end of file