From 836749c98ef1cab27b7effd8157031893ffabd45 Mon Sep 17 00:00:00 2001 From: Donne Martin Date: Thu, 2 Jul 2015 23:10:09 -0400 Subject: [PATCH] Added find loop start challenge. --- linked_lists/find_loop_start/__init__.py | 0 .../find_loop_start_challenge.ipynb | 209 ++++++++++++++++++ .../find_loop_start_solution.ipynb | 70 ++++-- .../find_loop_start/test_find_loop_start.py | 47 ++++ 4 files changed, 304 insertions(+), 22 deletions(-) create mode 100644 linked_lists/find_loop_start/__init__.py create mode 100644 linked_lists/find_loop_start/find_loop_start_challenge.ipynb create mode 100644 linked_lists/find_loop_start/test_find_loop_start.py diff --git a/linked_lists/find_loop_start/__init__.py b/linked_lists/find_loop_start/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/linked_lists/find_loop_start/find_loop_start_challenge.ipynb b/linked_lists/find_loop_start/find_loop_start_challenge.ipynb new file mode 100644 index 0000000..b5789ee --- /dev/null +++ b/linked_lists/find_loop_start/find_loop_start_challenge.ipynb @@ -0,0 +1,209 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://github.com/donnemartin/coding-challenges)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Challenge Notebook" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Problem: Find the start of a linked list loop.\n", + "\n", + "* [Constraints](#Constraints)\n", + "* [Test Cases](#Test-Cases)\n", + "* [Algorithm](#Algorithm)\n", + "* [Code](#Code)\n", + "* [Unit Test](#Unit-Test)\n", + "* [Solution Notebook](#Solution-Notebook)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Constraints\n", + "\n", + "*Problem statements are sometimes ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n", + "\n", + "* Is this a singly linked list?\n", + " * Yes\n", + "* Can we assume we are always passed a circular linked list?\n", + " * No\n", + "* Can we assume we already have a linked list class that can be used for this problem?\n", + " * Yes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test Cases\n", + "\n", + "* Empty list -> None\n", + "* Not a circular linked list -> None\n", + " * One element\n", + " * Two elements\n", + " * Three or more elements\n", + "* General case" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Algorithm\n", + "\n", + "Refer to the [Solution Notebook](http://nbviewer.ipython.org/github/donnemartin/coding-challenges/blob/master/linked_lists/find_loop_start/find_loop_start_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": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%run ../linked_list/linked_list.py\n", + "%load ../linked_list/linked_list.py" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class MyLinkedList(LinkedList):\n", + " \n", + " def find_loop_start(self):\n", + " # TODO: Implement me\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Unit Test" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "**The following unit test is expected to fail until you solve the challenge.**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# %load test_find_loop_start.py\n", + "from nose.tools import assert_equal\n", + "\n", + "\n", + "class TestFindLoopStart(object):\n", + " \n", + " def test_find_loop_start(self):\n", + " print('Test: Empty list')\n", + " linked_list = MyLinkedList()\n", + " assert_equal(linked_list.find_loop_start(), None)\n", + " \n", + " print('Test: Not a circular linked list: One element')\n", + " head = Node(1)\n", + " linked_list = MyLinkedList(head)\n", + " assert_equal(linked_list.find_loop_start(), None)\n", + " \n", + " print('Test: Not a circular linked list: Two elements')\n", + " linked_list.append(2)\n", + " assert_equal(linked_list.find_loop_start(), None)\n", + " \n", + " print('Test: Not a circular linked list: Three or more elements')\n", + " linked_list.append(3)\n", + " assert_equal(linked_list.find_loop_start(), None)\n", + " \n", + " print('Test: General case: Circular linked list')\n", + " node10 = Node(10)\n", + " node9 = Node(9, node10)\n", + " node8 = Node(8, node9)\n", + " node7 = Node(7, node8)\n", + " node6 = Node(6, node7)\n", + " node5 = Node(5, node6)\n", + " node4 = Node(4, node5)\n", + " node3 = Node(3, node4)\n", + " node2 = Node(2, node3)\n", + " node1 = Node(1, node2)\n", + " node0 = Node(0, node1)\n", + " node10.next = node3\n", + " linked_list = MyLinkedList(node0)\n", + " assert_equal(linked_list.find_loop_start(), 3)\n", + " \n", + " print('Success: test_find_loop_start')\n", + "\n", + "def main():\n", + " test = TestFindLoopStart()\n", + " test.test_find_loop_start()\n", + " \n", + "if __name__ == '__main__':\n", + " main()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Solution Notebook\n", + "\n", + "Review the [Solution Notebook](http://nbviewer.ipython.org/github/donnemartin/coding-challenges/blob/master/linked_lists/find_loop_start/find_loop_start_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/linked_lists/find_loop_start/find_loop_start_solution.ipynb b/linked_lists/find_loop_start/find_loop_start_solution.ipynb index b8bfa55..2b0f65c 100644 --- a/linked_lists/find_loop_start/find_loop_start_solution.ipynb +++ b/linked_lists/find_loop_start/find_loop_start_solution.ipynb @@ -4,7 +4,14 @@ "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)." + "This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://github.com/donnemartin/coding-challenges)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Solution Notebook" ] }, { @@ -26,9 +33,9 @@ "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", + "*Problem statements are sometimes ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n", "\n", - "* This is a singly linked list?\n", + "* Is this a singly linked list?\n", " * Yes\n", "* Can we assume we are always passed a circular linked list?\n", " * No\n", @@ -42,8 +49,8 @@ "source": [ "## Test Cases\n", "\n", - "* Empty list\n", - "* Not a circular linked list\n", + "* Empty list -> None\n", + "* Not a circular linked list -> None\n", " * One element\n", " * Two elements\n", " * Three or more elements\n", @@ -84,7 +91,7 @@ }, "outputs": [], "source": [ - "%run linked_list.py" + "%run ../linked_list/linked_list.py" ] }, { @@ -96,6 +103,7 @@ "outputs": [], "source": [ "class MyLinkedList(LinkedList):\n", + " \n", " def find_loop_start(self):\n", " if self.head is None or self.head.next is None:\n", " return\n", @@ -130,13 +138,6 @@ "## Unit Test" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "*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, @@ -148,19 +149,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Test: Empty list\n", - "Test: Not a circular linked list: One element\n", - "Test: Not a circular linked list: Two elements\n", - "Test: Not a circular linked list: Three or more elements\n", - "Test: General case: Circular linked list\n", - "Success: test_find_loop_start\n" + "Overwriting test_find_loop_start.py\n" ] } ], "source": [ + "%%writefile test_find_loop_start.py\n", "from nose.tools import assert_equal\n", "\n", - "class Test(object):\n", + "\n", + "class TestFindLoopStart(object):\n", + " \n", " def test_find_loop_start(self):\n", " print('Test: Empty list')\n", " linked_list = MyLinkedList()\n", @@ -197,9 +196,36 @@ " \n", " print('Success: test_find_loop_start')\n", "\n", + "def main():\n", + " test = TestFindLoopStart()\n", + " test.test_find_loop_start()\n", + " \n", "if __name__ == '__main__':\n", - " test = Test()\n", - " test.test_find_loop_start()" + " main()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Test: Empty list\n", + "Test: Not a circular linked list: One element\n", + "Test: Not a circular linked list: Two elements\n", + "Test: Not a circular linked list: Three or more elements\n", + "Test: General case: Circular linked list\n", + "Success: test_find_loop_start\n" + ] + } + ], + "source": [ + "%run -i test_find_loop_start.py" ] } ], diff --git a/linked_lists/find_loop_start/test_find_loop_start.py b/linked_lists/find_loop_start/test_find_loop_start.py new file mode 100644 index 0000000..c113480 --- /dev/null +++ b/linked_lists/find_loop_start/test_find_loop_start.py @@ -0,0 +1,47 @@ +from nose.tools import assert_equal + + +class TestFindLoopStart(object): + + def test_find_loop_start(self): + print('Test: Empty list') + linked_list = MyLinkedList() + assert_equal(linked_list.find_loop_start(), None) + + print('Test: Not a circular linked list: One element') + head = Node(1) + linked_list = MyLinkedList(head) + assert_equal(linked_list.find_loop_start(), None) + + print('Test: Not a circular linked list: Two elements') + linked_list.append(2) + assert_equal(linked_list.find_loop_start(), None) + + print('Test: Not a circular linked list: Three or more elements') + linked_list.append(3) + assert_equal(linked_list.find_loop_start(), None) + + print('Test: General case: Circular linked list') + node10 = Node(10) + node9 = Node(9, node10) + node8 = Node(8, node9) + node7 = Node(7, node8) + node6 = Node(6, node7) + node5 = Node(5, node6) + node4 = Node(4, node5) + node3 = Node(3, node4) + node2 = Node(2, node3) + node1 = Node(1, node2) + node0 = Node(0, node1) + node10.next = node3 + linked_list = MyLinkedList(node0) + assert_equal(linked_list.find_loop_start(), 3) + + print('Success: test_find_loop_start') + +def main(): + test = TestFindLoopStart() + test.test_find_loop_start() + +if __name__ == '__main__': + main() \ No newline at end of file