{
 "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": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "%run stack.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "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": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "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())"
   ]
  }
 ],
 "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
}
