{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://bit.ly/code-notes)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Problem: Implement a queue using two stacks.\n", "\n", "* [Constraints and Assumptions](#Constraints-and-Assumptions)\n", "* [Test Cases](#Test-Cases)\n", "* [Algorithm](#Algorithm)\n", "* [Code](#Code)\n", "* [Unit Test](#Unit-Test)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Constraints\n", "\n", "*Problem statements are often intentionally ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n", "\n", "* Do you expect the methods to be enqueue and dequeue?\n", " * Yes\n", "* Can we assume we already have a stack class that can be used for this problem?\n", " * Yes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Test Cases\n", "\n", "* Enqueue and dequeue on empty stack\n", "* Enqueue and dequeue on non-empty stack\n", "* Multiple enqueue in a row\n", "* Multiple dequeue in a row\n", "* Enqueue after a dequeue\n", "* Dequeue after an enqueue" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Algorithm\n", "\n", "We'll use two stacks (left and right) to implement the queue. The left stack will be used for enqueue and the right stack will be used for dequeue.\n", "\n", "To prevent multiple dequeue calls from needlessly shifting elements around between the stacks, we'll shift elements in a lazy manner.\n", "\n", "### Enqueue\n", "\n", "* If the left stack is empty and the right stack is not empty\n", " * Shift the elements of the right stack to the left stack\n", "* Push the data to the left stack\n", "\n", "Complexity:\n", "* Time: O(n)\n", "* Space: O(n)\n", "\n", "### Dequeue\n", "\n", "* If the right stack is empty and the the left stack is not empty\n", " * Shift the elements of the left stack to the right stack\n", "* Pop from the right stack and return the data\n", "\n", "Complexity:\n", "* Time: O(n)\n", "* Space: O(n)\n", "\n", "### Shift Stacks\n", "\n", "* While the source stack has elements:\n", " * Pop from the source stack and push the data to the destination stack\n", "\n", "Complexity:\n", "* Time: O(n)\n", "* Space: O(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Code" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "%run stack.py" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class QueueFromStacks(object):\n", " def __init__(self):\n", " self.left_stack = Stack()\n", " self.right_stack = Stack()\n", "\n", " def shift_stacks(self, source, destination):\n", " while source.peek() is not None:\n", " destination.push(source.pop())\n", "\n", " def enqueue(self, data):\n", " if self.left_stack.is_empty() and not self.right_stack.is_empty():\n", " self.shift_stacks(self.right_stack, self.left_stack)\n", " self.left_stack.push(data)\n", "\n", " def dequeue(self):\n", " if self.right_stack.is_empty() and not self.left_stack.is_empty():\n", " self.shift_stacks(self.left_stack, self.right_stack)\n", " return self.right_stack.pop()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Unit Test\n", "\n", "*It is important to identify and run through general and edge cases from the [Test Cases](#Test-Cases) section by hand. You generally will not be asked to write a unit test like what is shown below.*" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Test: Dequeue on empty stack\n", "Test: Enqueue on empty stack\n", "Test: Enqueue on non-empty stack\n", "Test: Multiple enqueue in a row\n", "Test: Dequeue on non-empty stack\n", "Test: Dequeue after an enqueue\n", "Test: Multiple dequeue in a row\n", "Test: Enqueue after a dequeue\n", "Success: test_queue_from_stacks\n" ] } ], "source": [ "from nose.tools import assert_equal\n", "\n", "class Test(object):\n", " def test_queue_from_stacks(self):\n", " print('Test: Dequeue on empty stack')\n", " queue = QueueFromStacks()\n", " assert_equal(queue.dequeue(), None)\n", "\n", " print('Test: Enqueue on empty stack')\n", " print('Test: Enqueue on non-empty stack')\n", " print('Test: Multiple enqueue in a row')\n", " num_items = 3\n", " for i in range (0, num_items):\n", " queue.enqueue(i)\n", "\n", " print('Test: Dequeue on non-empty stack')\n", " print('Test: Dequeue after an enqueue')\n", " assert_equal(queue.dequeue(), 0)\n", "\n", " print('Test: Multiple dequeue in a row')\n", " assert_equal(queue.dequeue(), 1)\n", " assert_equal(queue.dequeue(), 2)\n", "\n", " print('Test: Enqueue after a dequeue')\n", " queue.enqueue(5)\n", " assert_equal(queue.dequeue(), 5)\n", "\n", " print('Success: test_queue_from_stacks')\n", "\n", "if __name__ == '__main__':\n", " test = Test()\n", " test.test_queue_from_stacks()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.10" } }, "nbformat": 4, "nbformat_minor": 0 }