diff --git a/README.md b/README.md index 76bad2f..538da47 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ Continually updated IPython Notebooks containing coding problems and solutions ( * [Implement a stack that keeps track of its minimum element](http://nbviewer.ipython.org/github/donnemartin/algorithms-data-structures/blob/master/stacks-queues/stack-min.ipynb) * [Implement a set of stacks class that wraps a list of stacks, each bound by a capacity](http://nbviewer.ipython.org/github/donnemartin/algorithms-data-structures/blob/master/stacks-queues/set-of-stacks.ipynb) * [Implement the Towers of Hanoi with 3 towers and N disks](http://nbviewer.ipython.org/github/donnemartin/algorithms-data-structures/blob/master/stacks-queues/hanoi.ipynb) +* [Implement a queue using two stacks](http://nbviewer.ipython.org/github/donnemartin/algorithms-data-structures/blob/master/stacks-queues/queue-from-stacks.ipynb) ## Hacker Rank diff --git a/stacks-queues/queue-from-stacks.ipynb b/stacks-queues/queue-from-stacks.ipynb new file mode 100644 index 0000000..c08afe0 --- /dev/null +++ b/stacks-queues/queue-from-stacks.ipynb @@ -0,0 +1,208 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Problem: Implement a queue using two stacks.\n", + "\n", + "* [Clarifying Questions](#Clarifying-Questions)\n", + "* [Test Cases](#Test-Cases)\n", + "* [Algorithm](#Algorithm)\n", + "* [Code](#Code)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Clarifying Questions\n", + "\n", + "* Do you expect the methods to be enqueue and dequeue?\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": 3, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%run stack.py" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "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 is_stack_empty(self, stack):\n", + " return stack.peek() is None\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.is_stack_empty(self.left_stack) and not self.is_stack_empty(self.right_stack):\n", + " self.shift_stacks(self.right_stack, self.left_stack)\n", + " self.left_stack.push(data)\n", + "\n", + " def dequeue(self):\n", + " if self.is_stack_empty(self.right_stack) and not self.is_stack_empty(self.left_stack):\n", + " self.shift_stacks(self.left_stack, self.right_stack)\n", + " return self.right_stack.pop()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dequeue on empty stack\n", + "None\n", + "Enqueue on empty stack\n", + "Enqueue on non-empty stack\n", + "Multiple enqueue in a row\n", + "Dequeue on non-empty stack\n", + "Dequeue after an enqueue\n", + "0\n", + "Multiple dequeue in a row\n", + "1\n", + "2\n", + "Enqueue after a dequeue\n", + "5\n", + "None\n" + ] + } + ], + "source": [ + "print('Dequeue on empty stack')\n", + "queue = QueueFromStacks()\n", + "print(queue.dequeue())\n", + "print('Enqueue on empty stack')\n", + "print('Enqueue on non-empty stack')\n", + "print('Multiple enqueue in a row')\n", + "num_items = 3\n", + "for i in range (0, num_items):\n", + " queue.enqueue(i)\n", + "print('Dequeue on non-empty stack')\n", + "print('Dequeue after an enqueue')\n", + "print(queue.dequeue())\n", + "print('Multiple dequeue in a row')\n", + "print(queue.dequeue())\n", + "print(queue.dequeue())\n", + "print('Enqueue after a dequeue')\n", + "queue.enqueue(5)\n", + "print(queue.dequeue())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "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.9" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +}