Renamed top-level folders to use underscores instead of dashes.

This commit is contained in:
Donne Martin
2015-06-28 06:39:24 -04:00
parent d44ed31ef9
commit 7573730e39
53 changed files with 0 additions and 0 deletions

0
linked_lists/__init__.py Normal file
View File

View File

@@ -0,0 +1,235 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<small><i>This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://bit.ly/code-notes).</i></small>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem: Add two numbers whose digits are stored in a linked list in reverse order.\n",
"\n",
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
"* [Test Cases](#Test-Cases)\n",
"* [Algorithm](#Algorithm)\n",
"* [Code](#Code)\n",
"* [Unit Test](#Unit-Test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and Assumptions\n",
"\n",
"*Problem statements are often intentionally ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n",
"\n",
"* Do you expect the return to be in reverse order too?\n",
" * Yes\n",
"* What if one of the inputs is NULL?\n",
" * Return NULL for an invalid operation\n",
"* How large are these numbers--can they fit in memory?\n",
" * Yes\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(s)\n",
"* Add values of different lengths\n",
" * Input 1: 6->5->None\n",
" * Input 2: 9->8->7\n",
" * Result: 5->4->8\n",
"* Add values of same lengths\n",
" * Exercised from values of different lengths\n",
" * Done here for completeness"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm\n",
"\n",
"We could solve this with an iterative or a recursive algorithm, both are well suited for this exercise. We'll use a recursive algorithm for practice with recursion. Note this takes an extra space of O(m) where m is the recursion depth.\n",
"\n",
"* Base case:\n",
" * if first and second lists are NULL AND carry is zero\n",
" * Return NULL\n",
"* Recursive case:\n",
" * value = carry\n",
" * value += first.data + second.data\n",
" * remainder = value % 10\n",
" * new_carry = 1 if value >= 10, else 0\n",
" * Create a node with the remainder\n",
" * node.next = self.add(first.next, second.next, new_carry)\n",
" * Return node\n",
"\n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: O(n), extra space for result and recursion depth\n",
"\n",
"Notes:\n",
"* Careful with adding if the lists differ\n",
" * Only add if a node is not NULL\n",
" * Alternatively, we could add trailing zeroes to the smaller list"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%run linked_list.py"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class MyLinkedList(LinkedList):\n",
" def __add__(self, first_node, second_node, carry):\n",
" if type(carry) != int and carry < 0:\n",
" raise ValueError('Invalid int argument: carry')\n",
" if first_node is None and second_node is None and carry == 0:\n",
" return None\n",
" value = carry\n",
" value += first_node.data if first_node is not None else 0\n",
" value += second_node.data if second_node is not None else 0\n",
" remainder = value % 10\n",
" new_carry = 1 if value >= 10 else 0\n",
" node = Node(remainder)\n",
" node.next = self.__add__(first_node.next if first_node is not None else None, \n",
" second_node.next if first_node is not None else None, \n",
" new_carry)\n",
" return node\n",
"\n",
" def add(self, first_list, second_list):\n",
" if first_list is None or second_list is None:\n",
" return None\n",
" head = self.__add__(first_list.head, second_list.head, 0)\n",
" return MyLinkedList(head)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 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,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Test: Empty list(s)\n",
"Test: Add values of different lengths\n",
"Test: Add values of same lengths\n",
"Success: test_add\n"
]
}
],
"source": [
"from nose.tools import assert_equal\n",
"\n",
"class Test(object):\n",
" def test_add(self):\n",
" print('Test: Empty list(s)')\n",
" assert_equal(MyLinkedList().add(None, None), None)\n",
" assert_equal(MyLinkedList().add(Node(5), None), None)\n",
" assert_equal(MyLinkedList().add(None, Node(10)), None)\n",
"\n",
" print('Test: Add values of different lengths')\n",
" # Input 1: 6->5->None\n",
" # Input 2: 9->8->7\n",
" # Result: 5->4->8\n",
" first_list = MyLinkedList(Node(6))\n",
" first_list.append(5)\n",
" second_list = MyLinkedList(Node(9))\n",
" second_list.append(8)\n",
" second_list.append(7)\n",
" result = MyLinkedList().add(first_list, second_list)\n",
" assert_equal(result.get_all_data(), [5, 4, 8])\n",
"\n",
" print('Test: Add values of same lengths')\n",
" # Input 1: 6->5->4\n",
" # Input 2: 9->8->7\n",
" # Result: 5->4->2->1\n",
" first_head = Node(6)\n",
" first_list = MyLinkedList(first_head)\n",
" first_list.append(5)\n",
" first_list.append(4)\n",
" second_head = Node(9)\n",
" second_list = MyLinkedList(second_head)\n",
" second_list.append(8)\n",
" second_list.append(7)\n",
" result = MyLinkedList().add(first_list, second_list)\n",
" assert_equal(result.get_all_data(), [5, 4, 2, 1])\n",
" \n",
" print('Success: test_add')\n",
"\n",
"if __name__ == '__main__':\n",
" test = Test()\n",
" test.test_add()"
]
}
],
"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
}

View File

@@ -0,0 +1,198 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<small><i>This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://bit.ly/code-notes).</i></small>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem: Delete a node in the middle, given only access to that node.\n",
"\n",
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
"* [Test Cases](#Test-Cases)\n",
"* [Algorithm](#Algorithm)\n",
"* [Code](#Code)\n",
"* [Unit Test](#Unit-Test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and Assumptions\n",
"\n",
"*Problem statements are often intentionally ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n",
"\n",
"* What if the final node is being deleted, for example a single node list? Do we make it a dummy with a NULL value?\n",
" * Yes\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\n",
"* Null node to delete\n",
"* One node\n",
"* Multiple nodes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm\n",
"\n",
"We'll need two pointers, one to the current node and one to the next node. We will copy the next node's data to the current node's data (effectively deleting the current node) and update the current node's next pointer.\n",
"\n",
"* Check for error cases listed above\n",
"* set curr.data to next.data\n",
"* set curr.next to next.next\n",
"\n",
"Complexity:\n",
"* Time: O(1)\n",
"* Space: In-place"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%run linked_list.py"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class Node(object):\n",
" def __init__(self, data):\n",
" self.data = data\n",
" self.next = None\n",
"\n",
"class MyLinkedList(LinkedList):\n",
" def delete_node(self, node):\n",
" if self.head is None:\n",
" return\n",
" if node is None:\n",
" return\n",
" node_next = node.next\n",
" if node_next is None:\n",
" node.data = None\n",
" node.next = None\n",
" else:\n",
" node.data = node_next.data\n",
" node.next = node_next.next"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 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,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Test: Empty list, null node to delete\n",
"Test: One node\n",
"Test: Multiple nodes\n",
"Success: test_delete_node\n"
]
}
],
"source": [
"from nose.tools import assert_equal\n",
"\n",
"class Test(object):\n",
" def test_delete_node(self):\n",
" print('Test: Empty list, null node to delete')\n",
" linked_list = MyLinkedList(None)\n",
" linked_list.delete_node(None)\n",
" assert_equal(linked_list.get_all_data(), [])\n",
"\n",
" print('Test: One node')\n",
" head = Node(2)\n",
" linked_list = MyLinkedList(head)\n",
" linked_list.delete_node(head)\n",
" assert_equal(linked_list.get_all_data(), [None])\n",
"\n",
" print('Test: Multiple nodes')\n",
" linked_list = MyLinkedList(None) \n",
" node0 = linked_list.insert_to_front(1)\n",
" node1 = linked_list.insert_to_front(3)\n",
" node2 = linked_list.insert_to_front(4)\n",
" node3 = linked_list.insert_to_front(1)\n",
" linked_list.delete_node(node2)\n",
" assert_equal(linked_list.get_all_data(), [1, 3, 1])\n",
" \n",
" print('Success: test_delete_node')\n",
"\n",
"if __name__ == '__main__':\n",
" test = Test()\n",
" test.test_delete_node()"
]
}
],
"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
}

View File

@@ -0,0 +1,227 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<small><i>This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://bit.ly/code-notes).</i></small>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem: Find the start of a linked list loop.\n",
"\n",
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
"* [Test Cases](#Test-Cases)\n",
"* [Algorithm](#Algorithm)\n",
"* [Code](#Code)\n",
"* [Unit Test](#Unit-Test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and Assumptions\n",
"\n",
"*Problem statements are often intentionally ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n",
"\n",
"* This is 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\n",
"* Not a circular linked list\n",
" * One element\n",
" * Two elements\n",
" * Three or more elements\n",
"* General case"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm\n",
"\n",
"* Use two pointers i, j, initialized to the head\n",
"* Increment i and j until they meet\n",
" * j is incremented twice as fast as i\n",
" * If j's next is NULL, we do not have a circular list\n",
"* When i and j meet, move j to the head\n",
"* Increment i and j one node at a time until they meet\n",
"* Where they meet is the start of the loop\n",
"\n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: In-place"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%run linked_list.py"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class MyLinkedList(LinkedList):\n",
" def find_loop_start(self):\n",
" if self.head is None or self.head.next is None:\n",
" return\n",
" i = self.head\n",
" j = self.head\n",
" i = i.next\n",
" j = j.next.next\n",
" \n",
" # Increment i and j until they meet\n",
" # j is incremented twice as fast as i\n",
" while j != i:\n",
" i = i.next\n",
" if j is None or j.next is None:\n",
" return\n",
" j = j.next.next\n",
" \n",
" # When i and j meet, move j to the head\n",
" j = self.head\n",
" \n",
" # Increment i and j one node at a time until \n",
" # they meet, which is the start of the loop\n",
" while j != i:\n",
" i = i.next\n",
" j = j.next\n",
" return i.data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 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,
"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": [
"from nose.tools import assert_equal\n",
"\n",
"class Test(object):\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",
"if __name__ == '__main__':\n",
" test = Test()\n",
" test.test_find_loop_start()"
]
}
],
"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
}

View File

@@ -0,0 +1,209 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<small><i>This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://bit.ly/code-notes).</i></small>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem: Find the kth to last element of a linked list\n",
"\n",
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
"* [Test Cases](#Test-Cases)\n",
"* [Algorithm](#Algorithm)\n",
"* [Code](#Code)\n",
"* [Unit Test](#Unit-Test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and Assumptions\n",
"\n",
"*Problem statements are often intentionally ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n",
"\n",
"* Can we assume k is a valid integer?\n",
" * Yes\n",
"* If k = 0, does this return the last element?\n",
" * Yes\n",
"* What happens if k is greater than or equal to the length of the linked list?\n",
" * Return None\n",
"* Can you use additional data structures?\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\n",
"* k is not an integer\n",
"* k is >= the length of the linked list\n",
"* One element, k = 0\n",
"* General case with many elements, k < length of linked list"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm\n",
"\n",
"* Setup two pointers, current and previous\n",
"* Give current a headstart, incrementing it once for k = 1, twice for k = 2, etc\n",
"* Increment both pointers until current reaches the end\n",
"* Return the value of previous\n",
"\n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: In-place"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%run linked_list.py"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class MyLinkedList(LinkedList):\n",
" def kth_to_last_elem(self, k):\n",
" if self.head is None:\n",
" return\n",
" if k >= len(self):\n",
" return\n",
" curr = self.head\n",
" prev = self.head\n",
" counter = 0\n",
" \n",
" # Give current a headstart, incrementing it \n",
" # once for k = 1, twice for k = 2, etc\n",
" while counter < k:\n",
" curr = curr.next\n",
" counter += 1\n",
" if curr is None:\n",
" return\n",
" \n",
" # Increment both pointers until current reaches the end\n",
" while curr.next is not None:\n",
" curr = curr.next\n",
" prev = prev.next\n",
" return prev.data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 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,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Test: Empty list\n",
"Test: k >= len(list)\n",
"Test: One element, k = 0\n",
"Test: General case\n",
"Success: test_kth_to_last_elem\n"
]
}
],
"source": [
"from nose.tools import assert_equal\n",
"\n",
"class Test(object):\n",
" def test_kth_to_last_elem(self):\n",
" print('Test: Empty list')\n",
" linked_list = MyLinkedList(None)\n",
" assert_equal(linked_list.kth_to_last_elem(0), None)\n",
" \n",
" print('Test: k >= len(list)')\n",
" assert_equal(linked_list.kth_to_last_elem(100), None)\n",
" \n",
" print('Test: One element, k = 0')\n",
" head = Node(2)\n",
" linked_list = MyLinkedList(head)\n",
" assert_equal(linked_list.kth_to_last_elem(0), 2)\n",
" \n",
" print('Test: General case')\n",
" linked_list.insert_to_front(1)\n",
" linked_list.insert_to_front(3)\n",
" linked_list.insert_to_front(5)\n",
" linked_list.insert_to_front(7)\n",
" assert_equal(linked_list.kth_to_last_elem(2), 3)\n",
" \n",
" print('Success: test_kth_to_last_elem')\n",
"\n",
"if __name__ == '__main__':\n",
" test = Test()\n",
" test.test_kth_to_last_elem()"
]
}
],
"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
}

View File

@@ -0,0 +1,472 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<small><i>This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://bit.ly/code-notes).</i></small>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem: Implement a linked list with insert, append, find, delete, length, and print methods\n",
"\n",
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
"* [Test Cases](#Test-Cases)\n",
"* [Algorithm](#Algorithm)\n",
"* [Code](#Code)\n",
"* [Unit Test](#Unit-Test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and Assumptions\n",
"\n",
"*Problem statements are often intentionally 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",
"### Insert to Front\n",
"\n",
"* If the data we are inserting is NULL, return\n",
"* Create a node with the input data\n",
"* If this is an empty list\n",
" * Assign the head to the node\n",
"* Else\n",
" * Set the node.next to the head\n",
" * Set the head to the node\n",
"\n",
"Complexity:\n",
"* Time: O(1)\n",
"* Space: O(1), a new node is added\n",
"\n",
"### Append\n",
"\n",
"* If the data we are inserting is NULL, return\n",
"* Create a node with the input data\n",
"* If this is an empty list\n",
" * Assign the head to the node\n",
"* Else\n",
" * Iterate to the end of the list\n",
" * Set the final node's next to the new node\n",
"\n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: O(1), a new node is added\n",
"\n",
"### Find\n",
"\n",
"* If data we are finding is NULL, return\n",
"* If the list is empty, return\n",
"* For each node\n",
" * If the value is a match, return it\n",
" * Else, move on to the next node\n",
"\n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: In-place\n",
"\n",
"### Delete\n",
"\n",
"* If data we are deleting is NULL, return\n",
"* If the list is empty, return\n",
"* For each node, keep track of previous and current node\n",
" * If the value we are deleting is a match in the current node\n",
" * Update previous node's next pointer to the current node's next pointer\n",
" * We do not have have to explicitly delete in Python\n",
" * Else, move on to the next node\n",
"\n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: In-place\n",
"\n",
"### Length\n",
"\n",
"* For each node\n",
" * Increase length counter\n",
" \n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: In-place\n",
"\n",
"### Print\n",
"\n",
"* For each node\n",
" * Print the node's value\n",
" \n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: In-place"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Overwriting linked_list.py\n"
]
}
],
"source": [
"%%writefile linked_list.py\n",
"\n",
"class Node(object):\n",
" def __init__(self, data, next_node=None):\n",
" self.next = next_node\n",
" self.data = data\n",
" \n",
" def __str__(self):\n",
" return self.data\n",
"\n",
"class LinkedList(object):\n",
" def __init__(self, head=None):\n",
" self.head = head\n",
"\n",
" def __len__(self):\n",
" curr = self.head\n",
" counter = 0\n",
" while curr is not None:\n",
" counter += 1\n",
" curr = curr.next\n",
" return counter\n",
" \n",
" def insert_to_front(self, data):\n",
" if data is None:\n",
" return\n",
" node = Node(data)\n",
" if self.head is None:\n",
" self.head = node\n",
" else:\n",
" node.next = self.head\n",
" self.head = node\n",
" return node\n",
" \n",
" def append(self, data, next_node=None):\n",
" if data is None:\n",
" return\n",
" node = Node(data, next_node)\n",
" if self.head is None:\n",
" self.head = node\n",
" else:\n",
" curr_node = self.head\n",
" while curr_node.next is not None:\n",
" curr_node = curr_node.next\n",
" curr_node.next = node\n",
" return node\n",
" \n",
" def find(self, data):\n",
" if data is None:\n",
" return\n",
" if self.head is None:\n",
" return\n",
" curr_node = self.head\n",
" while curr_node is not None:\n",
" if curr_node.data == data:\n",
" return curr_node\n",
" else:\n",
" curr_node = curr_node.next\n",
" return\n",
" \n",
" def delete(self, data):\n",
" if data is None:\n",
" return\n",
" if self.head is None:\n",
" return\n",
" prev_node = self.head\n",
" curr_node = prev_node.next\n",
" while curr_node is not None:\n",
" if curr_node.data == data:\n",
" prev_node.next = curr_node.next\n",
" return\n",
" else:\n",
" prev_node = curr_node\n",
" curr_node = curr_node.next\n",
"\n",
" def print_list(self):\n",
" curr_node = self.head\n",
" while curr_node is not None:\n",
" print(curr_node.data)\n",
" curr_node = curr_node.next\n",
"\n",
" def get_all_data(self):\n",
" data = []\n",
" curr_node = self.head\n",
" while curr_node is not None:\n",
" data.append(curr_node.data)\n",
" curr_node = curr_node.next\n",
" return data"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%run linked_list.py"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 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,
"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": [
"from nose.tools import assert_equal\n",
"\n",
"class Test(object):\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",
"if __name__ == '__main__':\n",
" test = Test()\n",
" test.test_insert_to_front()\n",
" test.test_append()\n",
" test.test_find()\n",
" test.test_delete()\n",
" test.test_len()"
]
}
],
"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
}

View File

@@ -0,0 +1,86 @@
class Node(object):
def __init__(self, data, next_node=None):
self.next = next_node
self.data = data
def __str__(self):
return self.data
class LinkedList(object):
def __init__(self, head=None):
self.head = head
def __len__(self):
curr = self.head
counter = 0
while curr is not None:
counter += 1
curr = curr.next
return counter
def insert_to_front(self, data):
if data is None:
return
node = Node(data)
if self.head is None:
self.head = node
else:
node.next = self.head
self.head = node
return node
def append(self, data, next_node=None):
if data is None:
return
node = Node(data, next_node)
if self.head is None:
self.head = node
else:
curr_node = self.head
while curr_node.next is not None:
curr_node = curr_node.next
curr_node.next = node
return node
def find(self, data):
if data is None:
return
if self.head is None:
return
curr_node = self.head
while curr_node is not None:
if curr_node.data == data:
return curr_node
else:
curr_node = curr_node.next
return
def delete(self, data):
if data is None:
return
if self.head is None:
return
prev_node = self.head
curr_node = prev_node.next
while curr_node is not None:
if curr_node.data == data:
prev_node.next = curr_node.next
return
else:
prev_node = curr_node
curr_node = curr_node.next
def print_list(self):
curr_node = self.head
while curr_node is not None:
print(curr_node.data)
curr_node = curr_node.next
def get_all_data(self):
data = []
curr_node = self.head
while curr_node is not None:
data.append(curr_node.data)
curr_node = curr_node.next
return data

View File

@@ -0,0 +1,226 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<small><i>This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://bit.ly/code-notes).</i></small>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem: Determine if a linked list is a palindrome.\n",
"\n",
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
"* [Test Cases](#Test-Cases)\n",
"* [Algorithm](#Algorithm)\n",
"* [Code](#Code)\n",
"* [Unit Test](#Unit-Test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and Assumptions\n",
"\n",
"*Problem statements are often intentionally ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n",
"\n",
"* Is a single character or number a palindrome?\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",
"\n",
"* Empty list\n",
"* Single element list\n",
"* Two element list, not a palindrome\n",
"* Three element list, not a palindrome\n",
"* General case: Palindrome with even length\n",
"* General case: Palindrome with odd length"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm\n",
"\n",
"* Reverse the linked list\n",
"* Compare the reversed list with the original list\n",
" * Only need to compare the first half\n",
"\n",
"Complexity:\n",
"* Time: O(1)\n",
"* Space: O(n)\n",
"\n",
"Note:\n",
"* We could also do this iteratively, using a stack to effectively reverse the first half of the string."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%run linked_list.py"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class MyLinkedList(LinkedList):\n",
" def is_palindrome(self):\n",
" if self.head is None or self.head.next is None:\n",
" return False\n",
" curr = self.head\n",
" reversed_list = MyLinkedList()\n",
" length = 0\n",
" \n",
" # Reverse the linked list\n",
" while curr is not None:\n",
" reversed_list.insert_to_front(curr.data)\n",
" length += 1\n",
" curr = curr.next\n",
" \n",
" # Compare the reversed list with the original list\n",
" # Only need to compare the first half \n",
" iterations_to_compare_half = length / 2\n",
" curr = self.head\n",
" curr_reversed = reversed_list.head\n",
" for _ in xrange(0, iterations_to_compare_half):\n",
" if curr.data != curr_reversed.data:\n",
" return False\n",
" curr = curr.next\n",
" curr_reversed = curr_reversed.next\n",
" return True"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 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,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Test: Empty list\n",
"Test: Single element list\n",
"Test: Two element list, not a palindrome\n",
"Test: Three element list, not a palindrome\n",
"Test: General case: Palindrome with even length\n",
"Test: General case: Palindrome with odd length\n",
"Success: test_is_palindrome\n"
]
}
],
"source": [
"from nose.tools import assert_equal\n",
"\n",
"class Test(object):\n",
" def test_is_palindrome(self):\n",
" print('Test: Empty list')\n",
" linked_list = MyLinkedList()\n",
" assert_equal(linked_list.is_palindrome(), False)\n",
"\n",
" print('Test: Single element list')\n",
" head = Node(1)\n",
" linked_list = MyLinkedList(head)\n",
" assert_equal(linked_list.is_palindrome(), False)\n",
"\n",
" print('Test: Two element list, not a palindrome')\n",
" linked_list.append(2)\n",
" assert_equal(linked_list.is_palindrome(), False)\n",
"\n",
" print('Test: Three element list, not a palindrome')\n",
" linked_list.append(3)\n",
" assert_equal(linked_list.is_palindrome(), False)\n",
"\n",
" print('Test: General case: Palindrome with even length')\n",
" head = Node('a')\n",
" linked_list = MyLinkedList(head)\n",
" linked_list.append('b')\n",
" linked_list.append('b')\n",
" linked_list.append('a')\n",
" assert_equal(linked_list.is_palindrome(), True)\n",
"\n",
" print('Test: General case: Palindrome with odd length')\n",
" head = Node(1)\n",
" linked_list = MyLinkedList(head)\n",
" linked_list.append(2)\n",
" linked_list.append(3)\n",
" linked_list.append(2)\n",
" linked_list.append(1)\n",
" assert_equal(linked_list.is_palindrome(), True)\n",
" \n",
" print('Success: test_is_palindrome')\n",
"\n",
"if __name__ == '__main__':\n",
" test = Test()\n",
" test.test_is_palindrome()"
]
}
],
"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
}

View File

@@ -0,0 +1,224 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<small><i>This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://bit.ly/code-notes).</i></small>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem: Partition a linked list around a value x, such that all nodes less than x come before all nodes greater than or equal to x.\n",
"\n",
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
"* [Test Cases](#Test-Cases)\n",
"* [Algorithm](#Algorithm)\n",
"* [Code](#Code)\n",
"* [Unit Test](#Unit-Test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and Assumptions\n",
"\n",
"*Problem statements are often intentionally ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n",
"\n",
"* Can we create additional data structures?\n",
" * Yes\n",
"* Do you expect the function to return a new list?\n",
" * Yes\n",
"* Can we assume the input x is valid?\n",
" * Yes\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\n",
"* One element list\n",
"* Left linked list is empty\n",
"* Right linked list is empty\n",
"* General case\n",
" * Partition = 10\n",
" * Input: 4, 3, 7, 8, 10, 1, 10, 12\n",
" * Output: 4, 3, 7, 8, 1, 10, 10, 12"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm\n",
"\n",
"* Create left and right linked lists\n",
"* For each element in the list\n",
" * If elem < x, append to the left list\n",
" * else, append to the right list\n",
"* Merge left and right lists\n",
"\n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: O(n)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%run linked_list.py"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class MyLinkedList(LinkedList):\n",
" def partition(self, data):\n",
" if self.head is None:\n",
" return\n",
" left = MyLinkedList(None)\n",
" right = MyLinkedList(None)\n",
" curr = self.head\n",
" \n",
" # Build the left and right lists\n",
" while curr is not None:\n",
" if curr.data < data:\n",
" left.append(curr.data)\n",
" else:\n",
" right.append(curr.data)\n",
" curr = curr.next\n",
" curr_left = left.head\n",
" if curr_left is None:\n",
" return right\n",
" else:\n",
" # Merge the two lists\n",
" while curr_left.next is not None:\n",
" curr_left = curr_left.next\n",
" curr_left.next = right.head\n",
" return left"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 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,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Test: Empty list\n",
"Test: One element list, left list empty\n",
"Test: Right list is empty\n",
"Test: General case\n",
"Success: test_partition\n"
]
}
],
"source": [
"from nose.tools import assert_equal\n",
"\n",
"class Test(object):\n",
" def test_partition(self):\n",
" print('Test: Empty list')\n",
" linked_list = MyLinkedList(None)\n",
" linked_list.partition(10)\n",
" assert_equal(linked_list.get_all_data(), [])\n",
"\n",
" print('Test: One element list, left list empty')\n",
" linked_list = MyLinkedList(Node(5))\n",
" linked_list.partition(0)\n",
" assert_equal(linked_list.get_all_data(), [5])\n",
"\n",
" print('Test: Right list is empty')\n",
" linked_list = MyLinkedList(Node(5))\n",
" linked_list.partition(10)\n",
" assert_equal(linked_list.get_all_data(), [5])\n",
"\n",
" print('Test: General case')\n",
" # Partition = 10\n",
" # Input: 4, 3, 7, 8, 10, 1, 10, 12\n",
" # Output: 4, 3, 7, 8, 1, 10, 10, 12\n",
" linked_list = MyLinkedList(Node(12))\n",
" linked_list.insert_to_front(10)\n",
" linked_list.insert_to_front(1)\n",
" linked_list.insert_to_front(10)\n",
" linked_list.insert_to_front(8)\n",
" linked_list.insert_to_front(7)\n",
" linked_list.insert_to_front(3)\n",
" linked_list.insert_to_front(4)\n",
" partitioned_list = linked_list.partition(10)\n",
" assert_equal(partitioned_list.get_all_data(), \n",
" [4, 3, 7, 8, 1, 10, 10, 12])\n",
" \n",
" print('Success: test_partition')\n",
"\n",
"if __name__ == '__main__':\n",
" test = Test()\n",
" test.test_partition()"
]
}
],
"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
}

View File

@@ -0,0 +1,264 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<small><i>This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://bit.ly/code-notes).</i></small>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem: Remove duplicates from a linked list\n",
"\n",
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
"* [Test Cases](#Test-Cases)\n",
"* [Algorithm: Hash Map Lookup](#Algorithm:-Hash-Map-Lookup)\n",
"* [Code: Hash Map Lookup](#Code:-Hash-Map-Lookup)\n",
"* [Algorithm: In-Place](#Algorithm:-In-Place)\n",
"* [Code: In-Place](#Code:-In-Place)\n",
"* [Unit Test](#Unit-Test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and Assumptions\n",
"\n",
"*Problem statements are often intentionally 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",
"* Can you insert NULL values in the list?\n",
" * No\n",
"* Can you use additional data structures?\n",
" * Implement both solutions\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 linked list\n",
"* One element linked list\n",
"* Multiple elements\n",
"* No duplicates\n",
"* One or more duplicates"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm: Hash Map Lookup\n",
"\n",
"* For each node\n",
" * If the node's value is in the hash map\n",
" * Delete the node\n",
" * Else\n",
" * Add node's value to the hash map\n",
"\n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: O(m) where m is the number of values in the hash map\n",
"\n",
"Note:\n",
"* Deletion requires two pointers, one to the previous node and one to the current node"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code: Hash Map Lookup"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%run linked_list.py"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class MyLinkedList(LinkedList):\n",
" def remove_dupes(self):\n",
" seen_data = set()\n",
" curr = self.head\n",
" prev = None\n",
" while curr is not None:\n",
" if curr.data in seen_data:\n",
" prev.next = curr.next\n",
" else:\n",
" seen_data.add(curr.data)\n",
" prev = curr\n",
" curr = curr.next"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm: In-Place\n",
"\n",
"* For each node\n",
" * Compare node with every other node\n",
" * If the node's value is in the hash map\n",
" * Delete the node\n",
" * Else\n",
" * Add node's value to the hash map\n",
"\n",
"Complexity:\n",
"* Time: O(n^2)\n",
"* Space: In-place\n",
"\n",
"Note:\n",
"* Deletion requires two pointers, one to the previous node and one to the current node\n",
"* We'll need to use a 'runner' to check every other node and compare it to the current node"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code: In-Place"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class MyLinkedListAlt(LinkedList):\n",
" def remove_dupes(self):\n",
" curr = self.head\n",
" while curr is not None:\n",
" runner = curr\n",
" while runner.next is not None:\n",
" if runner.next.data == curr.data:\n",
" runner.next = runner.next.next\n",
" else:\n",
" runner = runner.next\n",
" curr = curr.next"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 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": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Test: Empty list\n",
"Test: One element list\n",
"Test: General case, duplicates\n",
"Test: General case, no duplicates\n",
"Success: test_remove_dupes\n",
"\n",
"Test: Empty list\n",
"Test: One element list\n",
"Test: General case, duplicates\n",
"Test: General case, no duplicates\n",
"Success: test_remove_dupes\n",
"\n"
]
}
],
"source": [
"from nose.tools import assert_equal\n",
"\n",
"class Test(object):\n",
" def test_remove_dupes(self, linked_list):\n",
" print('Test: Empty list')\n",
" linked_list.remove_dupes()\n",
" assert_equal(linked_list.get_all_data(), [])\n",
"\n",
" print('Test: One element list')\n",
" linked_list.insert_to_front(2)\n",
" linked_list.remove_dupes()\n",
" assert_equal(linked_list.get_all_data(), [2])\n",
"\n",
" print('Test: General case, duplicates')\n",
" linked_list.insert_to_front(1)\n",
" linked_list.insert_to_front(3)\n",
" linked_list.insert_to_front(1)\n",
" linked_list.insert_to_front(1)\n",
" linked_list.remove_dupes()\n",
" assert_equal(linked_list.get_all_data(), [1, 3, 2])\n",
"\n",
" print('Test: General case, no duplicates')\n",
" linked_list.remove_dupes()\n",
" assert_equal(linked_list.get_all_data(), [1, 3, 2])\n",
" \n",
" print('Success: test_remove_dupes\\n')\n",
"\n",
"if __name__ == '__main__':\n",
" test = Test()\n",
" linked_list = MyLinkedList(None)\n",
" test.test_remove_dupes(linked_list)\n",
" linked_list_alt = MyLinkedListAlt(None)\n",
" test.test_remove_dupes(linked_list_alt)"
]
}
],
"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
}

View File