From a6c7f71379591f908c78e4069510e226355bf7ef Mon Sep 17 00:00:00 2001 From: Donne Martin Date: Sat, 1 Aug 2015 09:25:14 -0400 Subject: [PATCH] Added binary search tree challenge. --- graphs_trees/bst/__init__.py | 0 graphs_trees/bst/bst.py | 53 ++++++ graphs_trees/bst/bst_challenge.ipynb | 211 ++++++++++++++++++++++++ graphs_trees/bst/bst_solution.ipynb | 238 +++++++++++++++++++++++++++ graphs_trees/bst/test_bst.py | 37 +++++ 5 files changed, 539 insertions(+) create mode 100644 graphs_trees/bst/__init__.py create mode 100644 graphs_trees/bst/bst.py create mode 100644 graphs_trees/bst/bst_challenge.ipynb create mode 100644 graphs_trees/bst/bst_solution.ipynb create mode 100644 graphs_trees/bst/test_bst.py diff --git a/graphs_trees/bst/__init__.py b/graphs_trees/bst/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/graphs_trees/bst/bst.py b/graphs_trees/bst/bst.py new file mode 100644 index 0000000..3b17225 --- /dev/null +++ b/graphs_trees/bst/bst.py @@ -0,0 +1,53 @@ +from collections import deque + + +class Node(object): + + def __init__(self, data): + self.data = data + self.left = None + self.right = None + + def insert(self, data): + if data <= self.data: + if self.left is None: + self.left = Node(data) + else: + self.left.insert(data) + else: + if self.right is None: + self.right = Node(data) + else: + self.right.insert(data) + + def in_order_traversal(self, visit_func): + if self.left is not None: + self.left.in_order_traversal(visit_func) + visit_func(self.data) + if self.right is not None: + self.right.in_order_traversal(visit_func) + + def pre_order_traversal(self, visit_func): + visit_func(self.data) + if self.left is not None: + self.left.pre_order_traversal(visit_func) + if self.right is not None: + self.right.pre_order_traversal(visit_func) + + def post_order_traversal(self, visit_func): + if self.left is not None: + self.left.post_order_traversal(visit_func) + if self.right is not None: + self.right.post_order_traversal(visit_func) + visit_func(self.data) + + def bfs(self, visit_func): + queue = deque() + queue.append(self) + while len(queue) > 0: + node = queue.popleft() + visit_func(node.data) + if node.left is not None: + queue.append(node.left) + if node.right is not None: + queue.append(node.right) \ No newline at end of file diff --git a/graphs_trees/bst/bst_challenge.ipynb b/graphs_trees/bst/bst_challenge.ipynb new file mode 100644 index 0000000..2dc27d7 --- /dev/null +++ b/graphs_trees/bst/bst_challenge.ipynb @@ -0,0 +1,211 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/interactive-coding-challenges)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Challenge Notebook" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Problem: Implement a binary search tree with an insert method.\n", + "\n", + "* [Constraints](#Constraints)\n", + "* [Test Cases](#Test-Cases)\n", + "* [Algorithm](#Algorithm)\n", + "* [Code](#Code)\n", + "* [Unit Test](#Unit-Test)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Constraints\n", + "\n", + "* Can we assume we are working with valid integers?\n", + " * Yes\n", + "* Can we assume all left descendents <= n < all right descendents?\n", + " * Yes\n", + "* For simplicity, can we use just a Node class without a wrapper Tree class?\n", + " * Yes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test Cases\n", + "\n", + "### Insert\n", + "\n", + "Insert will be tested through the following traversal:\n", + "\n", + "### In-Order Traversal (Provided)\n", + "\n", + "* 5, 2, 8, 1, 3 -> 1, 2, 3, 5, 8\n", + "* 1, 2, 3, 4, 5 -> 1, 2, 3, 4, 5" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Algorithm\n", + "\n", + "Refer to the [solution notebook](https://github.com/donnemartin/interactive-coding-challenges/graphs_trees/bst/bst_solution.ipynb). If you are stuck and need a hint, the solution notebook's algorithm discussion might be a good place to start." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Code" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note: `in_order_traversal` is provided for testing purposes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class Node(object):\n", + "\n", + " def __init__(self, data):\n", + " # TODO: Implement me\n", + "\n", + " def insert(self, data):\n", + " # TODO: Implement me\n", + "\n", + " def in_order_traversal(self, visit_func):\n", + " if self.left is not None:\n", + " self.left.in_order_traversal(visit_func)\n", + " visit_func(self.data)\n", + " if self.right is not None:\n", + " self.right.in_order_traversal(visit_func)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Unit Test" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**The following unit test is expected to fail until you solve the challenge.**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%run ../utils/captured_output.py" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# %load test_bst.py\n", + "from __future__ import print_function\n", + "from nose.tools import assert_equal\n", + "\n", + "\n", + "class TestTree(object):\n", + "\n", + " def test_tree(self):\n", + " root = Node(5)\n", + " root.insert(2)\n", + " root.insert(8)\n", + " root.insert(1)\n", + " root.insert(3)\n", + "\n", + " with captured_output() as (out, err):\n", + " root.in_order_traversal(sys.stdout.write)\n", + " assert_equal(out.getvalue().strip(), '12358')\n", + "\n", + " root = Node(1)\n", + " root.insert(2)\n", + " root.insert(3)\n", + " root.insert(4)\n", + " root.insert(5)\n", + "\n", + " with captured_output() as (out, err):\n", + " root.in_order_traversal(sys.stdout.write)\n", + " assert_equal(out.getvalue().strip(), '12345')\n", + "\n", + " print('Success: test_tree')\n", + "\n", + "\n", + "def main():\n", + " test = TestTree()\n", + " test.test_tree()\n", + "\n", + "\n", + "if __name__ == '__main__':\n", + " main()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Solution Notebook\n", + "\n", + "Review the [solution notebook](https://github.com/donnemartin/interactive-coding-challenges/graphs_trees/bst/bst_solution.ipynb) for a discussion on algorithms and code solutions." + ] + } + ], + "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 +} diff --git a/graphs_trees/bst/bst_solution.ipynb b/graphs_trees/bst/bst_solution.ipynb new file mode 100644 index 0000000..8d3862b --- /dev/null +++ b/graphs_trees/bst/bst_solution.ipynb @@ -0,0 +1,238 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/interactive-coding-challenges)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Solution Notebook" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Problem: Implement a binary search tree with an insert method.\n", + "\n", + "* [Constraints](#Constraints)\n", + "* [Test Cases](#Test-Cases)\n", + "* [Algorithm](#Algorithm)\n", + "* [Code](#Code)\n", + "* [Unit Test](#Unit-Test)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Constraints\n", + "\n", + "* Can we assume we are working with valid integers?\n", + " * Yes\n", + "* Can we assume all left descendents <= n < all right descendents?\n", + " * Yes\n", + "* For simplicity, can we use just a Node class without a wrapper Tree class?\n", + " * Yes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test Cases\n", + "\n", + "### Insert\n", + "\n", + "Insert will be tested through the following traversal:\n", + "\n", + "### In-Order Traversal (Provided)\n", + "\n", + "* 5, 2, 8, 1, 3 -> 1, 2, 3, 5, 8\n", + "* 1, 2, 3, 4, 5 -> 1, 2, 3, 4, 5" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Algorithm\n", + "\n", + "### Insert\n", + "\n", + "* If the data is <= the current node's data\n", + " * If the current node's left child is None, set it to Node(data)\n", + " * Else, recursively call insert on the left child\n", + "* Else\n", + " * If the current node's right child is None, set it to Node(data)\n", + " * Else, recursively call insert on the right child\n", + "\n", + "Complexity:\n", + "\n", + "* Time: O(log n)\n", + "* Space: O(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Code" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class Node(object):\n", + "\n", + " def __init__(self, data):\n", + " self.data = data\n", + " self.left = None\n", + " self.right = None\n", + "\n", + " def insert(self, data):\n", + " if data <= self.data:\n", + " if self.left is None:\n", + " self.left = Node(data)\n", + " else:\n", + " self.left.insert(data)\n", + " else:\n", + " if self.right is None:\n", + " self.right = Node(data)\n", + " else:\n", + " self.right.insert(data)\n", + "\n", + " def in_order_traversal(self, visit_func):\n", + " if self.left is not None:\n", + " self.left.in_order_traversal(visit_func)\n", + " visit_func(self.data)\n", + " if self.right is not None:\n", + " self.right.in_order_traversal(visit_func)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Unit Test" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%run ../utils/captured_output.py" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting test_bst.py\n" + ] + } + ], + "source": [ + "%%writefile test_bst.py\n", + "from __future__ import print_function\n", + "from nose.tools import assert_equal\n", + "\n", + "\n", + "class TestTree(object):\n", + "\n", + " def test_tree(self):\n", + " root = Node(5)\n", + " root.insert(2)\n", + " root.insert(8)\n", + " root.insert(1)\n", + " root.insert(3)\n", + "\n", + " with captured_output() as (out, err):\n", + " root.in_order_traversal(sys.stdout.write)\n", + " assert_equal(out.getvalue().strip(), '12358')\n", + "\n", + " root = Node(1)\n", + " root.insert(2)\n", + " root.insert(3)\n", + " root.insert(4)\n", + " root.insert(5)\n", + "\n", + " with captured_output() as (out, err):\n", + " root.in_order_traversal(sys.stdout.write)\n", + " assert_equal(out.getvalue().strip(), '12345')\n", + "\n", + " print('Success: test_tree')\n", + "\n", + "\n", + "def main():\n", + " test = TestTree()\n", + " test.test_tree()\n", + "\n", + "\n", + "if __name__ == '__main__':\n", + " main()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success: test_tree\n" + ] + } + ], + "source": [ + "%run -i test_bst.py" + ] + } + ], + "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 +} diff --git a/graphs_trees/bst/test_bst.py b/graphs_trees/bst/test_bst.py new file mode 100644 index 0000000..894fc9e --- /dev/null +++ b/graphs_trees/bst/test_bst.py @@ -0,0 +1,37 @@ +from __future__ import print_function +from nose.tools import assert_equal + + +class TestTree(object): + + def test_tree(self): + root = Node(5) + root.insert(2) + root.insert(8) + root.insert(1) + root.insert(3) + + with captured_output() as (out, err): + root.in_order_traversal(sys.stdout.write) + assert_equal(out.getvalue().strip(), '12358') + + root = Node(1) + root.insert(2) + root.insert(3) + root.insert(4) + root.insert(5) + + with captured_output() as (out, err): + root.in_order_traversal(sys.stdout.write) + assert_equal(out.getvalue().strip(), '12345') + + print('Success: test_tree') + + +def main(): + test = TestTree() + test.test_tree() + + +if __name__ == '__main__': + main() \ No newline at end of file