diff --git a/linked_lists/linked_list/linked_list.py b/linked_lists/linked_list/linked_list.py index f6406a8..d888321 100644 --- a/linked_lists/linked_list/linked_list.py +++ b/linked_lists/linked_list/linked_list.py @@ -1,5 +1,5 @@ - class Node(object): + def __init__(self, data, next_node=None): self.next = next_node self.data = data @@ -8,6 +8,7 @@ class Node(object): return self.data class LinkedList(object): + def __init__(self, head=None): self.head = head diff --git a/linked_lists/linked_list/linked_list_challenge.ipynb b/linked_lists/linked_list/linked_list_challenge.ipynb new file mode 100644 index 0000000..ed0549d --- /dev/null +++ b/linked_lists/linked_list/linked_list_challenge.ipynb @@ -0,0 +1,331 @@ +{ + "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: Implement a linked list with insert, append, find, delete, length, and print methods\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 or doubly linked list?\n", + " * Singly\n", + "* Is this a circular list?\n", + " * No\n", + "* Do we keep track of the tail or just the head?\n", + " * Just the head" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test Cases\n", + "\n", + "### Insert to Front\n", + "\n", + "* Insert a NULL\n", + "* Insert in an empty list\n", + "* Insert in a list with one element or more elements\n", + "\n", + "### Append\n", + "\n", + "* Append a NULL\n", + "* Append in an empty list\n", + "* Insert in a list with one element or more elements\n", + "\n", + "### Find\n", + "\n", + "* Find a NULL\n", + "* Find in an empty list\n", + "* Find in a list with one element or more matching elements\n", + "* Find in a list with no matches\n", + "\n", + "### Delete\n", + "\n", + "* Delete a NULL\n", + "* Delete in an empty list\n", + "* Delete in a list with one element or more matching elements\n", + "* Delete in a list with no matches\n", + "\n", + "### Length\n", + "\n", + "* Length of zero or more elements\n", + "\n", + "### Print\n", + "\n", + "* Print an empty list\n", + "* Print a list with one or more elements" + ] + }, + { + "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/linked_list/linked_list_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": false + }, + "outputs": [], + "source": [ + "class Node(object):\n", + " \n", + " def __init__(self, data, next_node=None):\n", + " pass\n", + " # TODO: Implement me\n", + " \n", + " def __str__(self):\n", + " pass\n", + " # TODO: Implement me\n", + "\n", + "class LinkedList(object):\n", + " \n", + " def __init__(self, head=None):\n", + " pass\n", + " # TODO: Implement me\n", + "\n", + " def __len__(self):\n", + " pass\n", + " # TODO: Implement me\n", + " \n", + " def insert_to_front(self, data):\n", + " pass\n", + " # TODO: Implement me\n", + " \n", + " def append(self, data, next_node=None):\n", + " pass\n", + " # TODO: Implement me\n", + " \n", + " def find(self, data):\n", + " pass\n", + " # TODO: Implement me\n", + " \n", + " def delete(self, data):\n", + " pass\n", + " # TODO: Implement me\n", + "\n", + " def print_list(self):\n", + " pass\n", + " # TODO: Implement me\n", + "\n", + " def get_all_data(self):\n", + " pass\n", + " # TODO: Implement me" + ] + }, + { + "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_linked_list.py\n", + "from nose.tools import assert_equal\n", + "\n", + "\n", + "class TestLinkedList(object):\n", + " \n", + " def test_insert_to_front(self):\n", + " print('Test: insert_to_front on an empty list')\n", + " linked_list = LinkedList(None)\n", + " linked_list.insert_to_front(10)\n", + " assert_equal(linked_list.get_all_data(), [10])\n", + "\n", + " print('Test: insert_to_front on a NULL')\n", + " linked_list.insert_to_front(None)\n", + " assert_equal(linked_list.get_all_data(), [10])\n", + "\n", + " print('Test: insert_to_front general case')\n", + " linked_list.insert_to_front('a')\n", + " linked_list.insert_to_front('bc')\n", + " assert_equal(linked_list.get_all_data(), ['bc', 'a', 10])\n", + " \n", + " print('Success: test_insert_to_front\\n')\n", + " \n", + " def test_append(self):\n", + " print('Test: append on an empty list')\n", + " linked_list = LinkedList(None)\n", + " linked_list.append(10)\n", + " assert_equal(linked_list.get_all_data(), [10])\n", + "\n", + " print('Test: append a NULL')\n", + " linked_list.append(None)\n", + " assert_equal(linked_list.get_all_data(), [10])\n", + "\n", + " print('Test: append general case')\n", + " linked_list.append('a')\n", + " linked_list.append('bc')\n", + " assert_equal(linked_list.get_all_data(), [10, 'a', 'bc'])\n", + " \n", + " print('Success: test_append\\n')\n", + " \n", + " def test_find(self):\n", + " print('Test: find on an empty list')\n", + " linked_list = LinkedList(None)\n", + " node = linked_list.find('a')\n", + " assert_equal(node, None)\n", + "\n", + " print('Test: find a NULL')\n", + " head = Node(10)\n", + " linked_list = LinkedList(head)\n", + " node = linked_list.find(None)\n", + " assert_equal(node, None)\n", + "\n", + " print('Test: find general case with matches')\n", + " head = Node(10)\n", + " linked_list = LinkedList(head)\n", + " linked_list.insert_to_front('a')\n", + " linked_list.insert_to_front('bc')\n", + " node = linked_list.find('a')\n", + " assert_equal(str(node), 'a')\n", + "\n", + " print('Test: find general case with no matches')\n", + " node = linked_list.find('aaa')\n", + " assert_equal(node, None)\n", + " \n", + " print('Success: test_find\\n')\n", + " \n", + " def test_delete(self):\n", + " print('Test: delete on an empty list')\n", + " linked_list = LinkedList(None)\n", + " linked_list.delete('a')\n", + " assert_equal(linked_list.get_all_data(), [])\n", + "\n", + " print('Test: delete a NULL')\n", + " head = Node(10)\n", + " linked_list = LinkedList(head)\n", + " linked_list.delete(None)\n", + " assert_equal(linked_list.get_all_data(), [10])\n", + "\n", + " print('Test: delete general case with matches')\n", + " head = Node(10)\n", + " linked_list = LinkedList(head)\n", + " linked_list.insert_to_front('a')\n", + " linked_list.insert_to_front('bc')\n", + " linked_list.delete('a')\n", + " assert_equal(linked_list.get_all_data(), ['bc', 10])\n", + "\n", + " print('Test: delete general case with no matches')\n", + " linked_list.delete('aa')\n", + " assert_equal(linked_list.get_all_data(), ['bc', 10])\n", + " \n", + " print('Success: test_delete\\n')\n", + " \n", + " def test_len(self):\n", + " print('Test: len on an empty list')\n", + " linked_list = LinkedList(None)\n", + " assert_equal(len(linked_list), 0)\n", + "\n", + " print('Test: len general case')\n", + " head = Node(10)\n", + " linked_list = LinkedList(head)\n", + " linked_list.insert_to_front('a')\n", + " linked_list.insert_to_front('bc')\n", + " assert_equal(len(linked_list), 3)\n", + " \n", + " print('Success: test_len\\n')\n", + "\n", + "def main():\n", + " test = TestLinkedList()\n", + " test.test_insert_to_front()\n", + " test.test_append()\n", + " test.test_find()\n", + " test.test_delete()\n", + " test.test_len()\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/linked_list/linked_list_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/linked_list/linked_list_solution.ipynb b/linked_lists/linked_list/linked_list_solution.ipynb index 2255f0e..58cae19 100644 --- a/linked_lists/linked_list/linked_list_solution.ipynb +++ b/linked_lists/linked_list/linked_list_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,7 +33,7 @@ "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", "* Is this a singly or doubly linked list?\n", " * Singly\n", @@ -181,8 +188,8 @@ ], "source": [ "%%writefile linked_list.py\n", - "\n", "class Node(object):\n", + " \n", " def __init__(self, data, next_node=None):\n", " self.next = next_node\n", " self.data = data\n", @@ -191,6 +198,7 @@ " return self.data\n", "\n", "class LinkedList(object):\n", + " \n", " def __init__(self, head=None):\n", " self.head = head\n", "\n", @@ -287,13 +295,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, @@ -305,39 +306,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Test: insert_to_front on an empty list\n", - "Test: insert_to_front on a NULL\n", - "Test: insert_to_front general case\n", - "Success: test_insert_to_front\n", - "\n", - "Test: append on an empty list\n", - "Test: append a NULL\n", - "Test: append general case\n", - "Success: test_append\n", - "\n", - "Test: find on an empty list\n", - "Test: find a NULL\n", - "Test: find general case with matches\n", - "Test: find general case with no matches\n", - "Success: test_find\n", - "\n", - "Test: delete on an empty list\n", - "Test: delete a NULL\n", - "Test: delete general case with matches\n", - "Test: delete general case with no matches\n", - "Success: test_delete\n", - "\n", - "Test: len on an empty list\n", - "Test: len general case\n", - "Success: test_len\n", - "\n" + "Overwriting test_linked_list.py\n" ] } ], "source": [ + "%%writefile test_linked_list.py\n", "from nose.tools import assert_equal\n", "\n", - "class Test(object):\n", + "\n", + "class TestLinkedList(object):\n", + " \n", " def test_insert_to_front(self):\n", " print('Test: insert_to_front on an empty list')\n", " linked_list = LinkedList(None)\n", @@ -438,13 +417,60 @@ " \n", " print('Success: test_len\\n')\n", "\n", - "if __name__ == '__main__':\n", - " test = Test()\n", + "def main():\n", + " test = TestLinkedList()\n", " test.test_insert_to_front()\n", " test.test_append()\n", " test.test_find()\n", " test.test_delete()\n", - " test.test_len()" + " test.test_len()\n", + " \n", + "if __name__ == '__main__':\n", + " main()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Test: insert_to_front on an empty list\n", + "Test: insert_to_front on a NULL\n", + "Test: insert_to_front general case\n", + "Success: test_insert_to_front\n", + "\n", + "Test: append on an empty list\n", + "Test: append a NULL\n", + "Test: append general case\n", + "Success: test_append\n", + "\n", + "Test: find on an empty list\n", + "Test: find a NULL\n", + "Test: find general case with matches\n", + "Test: find general case with no matches\n", + "Success: test_find\n", + "\n", + "Test: delete on an empty list\n", + "Test: delete a NULL\n", + "Test: delete general case with matches\n", + "Test: delete general case with no matches\n", + "Success: test_delete\n", + "\n", + "Test: len on an empty list\n", + "Test: len general case\n", + "Success: test_len\n", + "\n" + ] + } + ], + "source": [ + "%run -i test_linked_list.py" ] } ], diff --git a/linked_lists/linked_list/test_linked_list.py b/linked_lists/linked_list/test_linked_list.py new file mode 100644 index 0000000..5899f35 --- /dev/null +++ b/linked_lists/linked_list/test_linked_list.py @@ -0,0 +1,115 @@ +from nose.tools import assert_equal + + +class TestLinkedList(object): + + def test_insert_to_front(self): + print('Test: insert_to_front on an empty list') + linked_list = LinkedList(None) + linked_list.insert_to_front(10) + assert_equal(linked_list.get_all_data(), [10]) + + print('Test: insert_to_front on a NULL') + linked_list.insert_to_front(None) + assert_equal(linked_list.get_all_data(), [10]) + + print('Test: insert_to_front general case') + linked_list.insert_to_front('a') + linked_list.insert_to_front('bc') + assert_equal(linked_list.get_all_data(), ['bc', 'a', 10]) + + print('Success: test_insert_to_front\n') + + def test_append(self): + print('Test: append on an empty list') + linked_list = LinkedList(None) + linked_list.append(10) + assert_equal(linked_list.get_all_data(), [10]) + + print('Test: append a NULL') + linked_list.append(None) + assert_equal(linked_list.get_all_data(), [10]) + + print('Test: append general case') + linked_list.append('a') + linked_list.append('bc') + assert_equal(linked_list.get_all_data(), [10, 'a', 'bc']) + + print('Success: test_append\n') + + def test_find(self): + print('Test: find on an empty list') + linked_list = LinkedList(None) + node = linked_list.find('a') + assert_equal(node, None) + + print('Test: find a NULL') + head = Node(10) + linked_list = LinkedList(head) + node = linked_list.find(None) + assert_equal(node, None) + + print('Test: find general case with matches') + head = Node(10) + linked_list = LinkedList(head) + linked_list.insert_to_front('a') + linked_list.insert_to_front('bc') + node = linked_list.find('a') + assert_equal(str(node), 'a') + + print('Test: find general case with no matches') + node = linked_list.find('aaa') + assert_equal(node, None) + + print('Success: test_find\n') + + def test_delete(self): + print('Test: delete on an empty list') + linked_list = LinkedList(None) + linked_list.delete('a') + assert_equal(linked_list.get_all_data(), []) + + print('Test: delete a NULL') + head = Node(10) + linked_list = LinkedList(head) + linked_list.delete(None) + assert_equal(linked_list.get_all_data(), [10]) + + print('Test: delete general case with matches') + head = Node(10) + linked_list = LinkedList(head) + linked_list.insert_to_front('a') + linked_list.insert_to_front('bc') + linked_list.delete('a') + assert_equal(linked_list.get_all_data(), ['bc', 10]) + + print('Test: delete general case with no matches') + linked_list.delete('aa') + assert_equal(linked_list.get_all_data(), ['bc', 10]) + + print('Success: test_delete\n') + + def test_len(self): + print('Test: len on an empty list') + linked_list = LinkedList(None) + assert_equal(len(linked_list), 0) + + print('Test: len general case') + head = Node(10) + linked_list = LinkedList(head) + linked_list.insert_to_front('a') + linked_list.insert_to_front('bc') + assert_equal(len(linked_list), 3) + + print('Success: test_len\n') + +def main(): + test = TestLinkedList() + test.test_insert_to_front() + test.test_append() + test.test_find() + test.test_delete() + test.test_len() + +if __name__ == '__main__': + main() \ No newline at end of file