diff --git a/solutions/object_oriented_design/circular_array/__init__.py b/solutions/object_oriented_design/circular_array/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/solutions/object_oriented_design/circular_array/circular_array.ipynb b/solutions/object_oriented_design/circular_array/circular_array.ipynb new file mode 100644 index 00000000..7366c73e --- /dev/null +++ b/solutions/object_oriented_design/circular_array/circular_array.ipynb @@ -0,0 +1,222 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook was prepared by [Sarath S Pillai](https://github.com/sarathsp06). Source and license info is on [GitHub](https://github.com/donnemartin/system-design-primer-primer)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Design a circular array" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Constraints and assumptions\n", + "\n", + "* For simplicity, are the keys integers only?\n", + " * Yes\n", + "* Can we assume this fits memory?\n", + " * Yes\n", + "* Is array size fixed\n", + " * Yes\n", + "* What happens to deleted indexes\n", + " * None onget request" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Circular array Interface\n", + "* \\[idx\\]\n", + " * get item on idx ,assuming circular array\n", + " * any idx value is taken as modulo of itself with size\n", + " * negative idx is not permitted\n", + "* len\n", + " * length is always length of current valid entries\n", + "* repr\n", + " * the circular aray is printed with values in order\n", + " * logical order is maintained not how it is stored\n", + "* push\n", + " * pushed item to end of queue\n", + " * throws exception if capcity is filled , len == capcaity\n", + "* pop\n", + " * pop an item from start of array\n", + " * throws exception if \n", + "* capacity\n", + " * returns initial size of array " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Solution" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting circular_array.py\n" + ] + } + ], + "source": [ + "%%writefile circular_array.py\n", + "class CircularArray(object):\n", + " def __init__(self, size):\n", + " if size <= 0:\n", + " raise ValueError(\"Size has to be non zero positive number\")\n", + " self.size = size\n", + " self.array = self.size *[None]\n", + " self.start = self.end = None\n", + " \n", + " def capacity(self):\n", + " return self.size\n", + " \n", + " def __len__(self):\n", + " if self.start is None:\n", + " return 0\n", + " return (self.end - self.start + self.size ) % self.size + 1\n", + " \n", + " def __getitem__(self, key):\n", + " if key < 0:\n", + " raise ValueError(\"Key has to be a positive number\")\n", + " if self.start is None:\n", + " return None\n", + " index = (key + self.start) % self.size\n", + " return self.array[index]\n", + " \n", + " def push(self, value):\n", + " if self.start is None:\n", + " self.start = self.end = 0\n", + " else:\n", + " index = (self.end + 1) % self.size\n", + " if index == self.start:\n", + " raise IndexError(\"Push to full array \")\n", + " self.end = index\n", + " self.array[self.end] = value\n", + " \n", + " def __repr__(self):\n", + " if self.start is None:\n", + " return '[]'\n", + " if self.start < self.end:\n", + " return repr(self.array[self.start:self.end+1])\n", + " return repr(self.array[self.start:]+self.array[:self.end+1])\n", + " \n", + " def pop(self):\n", + " if self.start is None:\n", + " raise RuntimeException(\"Pop on nil array\")\n", + " value = self.array[self.start]\n", + " if self.start == self.end:\n", + " self.start = self.end = None\n", + " return value\n", + " self.start = (self.start + 1) % self.size\n", + " return value" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Test\n", + "\n", + "Here are basic test cases to illustrate the features" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "from circular_array import CircularArray\n", + "# create a circular array of 10 elements\n", + "ca = CircularArray(10);\n", + "assert ca.capacity() == 10\n", + "\n", + "#push 4 items\n", + "ca.push(1)\n", + "ca.push(2)\n", + "ca.push(3)\n", + "ca.push(4)\n", + "assert ca.capacity() == 10\n", + "\n", + "#pop and check 3 elements\n", + "assert ca.pop() == 1\n", + "assert ca.pop() == 2\n", + "assert ca.pop() == 3\n", + "assert len(ca) == 1\n", + "\n", + "#push 4 more elements\n", + "ca.push(5)\n", + "ca.push(6)\n", + "ca.push(7)\n", + "ca.push(8)\n", + "assert len(ca) == 5\n", + "\n", + "#check getitem function for proper indexing and pop 2 elements\n", + "assert ca[0] == 4\n", + "assert ca.pop() == 4\n", + "assert ca[0] == 5\n", + "assert ca[10] == 5\n", + "assert ca.pop() == 5\n", + "assert len(ca) == 3\n", + "#push 7 more items \n", + "ca.push(9)\n", + "ca.push(10)\n", + "ca.push(11)\n", + "ca.push(12)\n", + "ca.push(13)\n", + "ca.push(14)\n", + "ca.push(15)\n", + "assert len(ca) == 10\n", + "assert ca.capacity() == 10\n", + "assert repr(ca) == '[6, 7, 8, 9, 10, 11, 12, 13, 14, 15]'\n", + "assert ca[1] == 7\n", + "assert ca[11] == 7" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/solutions/object_oriented_design/circular_array/circular_array.py b/solutions/object_oriented_design/circular_array/circular_array.py new file mode 100644 index 00000000..1a8793fe --- /dev/null +++ b/solutions/object_oriented_design/circular_array/circular_array.py @@ -0,0 +1,50 @@ +class CircularArray(object): + def __init__(self, size): + if size <= 0: + raise ValueError("Size has to be non zero positive number") + self.size = size + self.array = self.size *[None] + self.start = self.end = None + + def capacity(self): + return self.size + + def __len__(self): + if self.start is None: + return 0 + return (self.end - self.start + self.size ) % self.size + 1 + + def __getitem__(self, key): + if key < 0: + raise ValueError("Key has to be a positive number") + if self.start is None: + return None + index = (key + self.start) % self.size + return self.array[index] + + def push(self, value): + if self.start is None: + self.start = self.end = 0 + else: + index = (self.end + 1) % self.size + if index == self.start: + raise IndexError("Push to full array ") + self.end = index + self.array[self.end] = value + + def __repr__(self): + if self.start is None: + return '[]' + if self.start < self.end: + return repr(self.array[self.start:self.end+1]) + return repr(self.array[self.start:]+self.array[:self.end+1]) + + def pop(self): + if self.start is None: + raise RuntimeException("Pop on nil array") + value = self.array[self.start] + if self.start == self.end: + self.start = self.end = None + return value + self.start = (self.start + 1) % self.size + return value \ No newline at end of file