From 25a38e09daacacd864ef8242fb4b2b59355a967a Mon Sep 17 00:00:00 2001 From: Julien Wilson Date: Wed, 18 Jan 2017 13:56:19 -0800 Subject: [PATCH 01/72] Update .travis.yml --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3abf556..9556c32 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,8 @@ language: python python: - "2.7" - "3.5" + install: - pip install -e .[test] -script: py.test +script: py.test src/test_bst.py From 97e30fc8508ee0091b775e3898841c3283e1664e Mon Sep 17 00:00:00 2001 From: Julien Wilson Date: Wed, 18 Jan 2017 13:57:56 -0800 Subject: [PATCH 02/72] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d4d8ec5..81a891e 100755 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Build Status](https://travis-ci.org/julienawilson/data-structures.svg?branch=bst)](https://travis-ci.org/julienawilson/data-structures) + # data-structures Patrick Saunders and Julien Wilson
From c8b6b90bd942c5d344e0696dff2210d4c817da20 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Wed, 18 Jan 2017 14:05:26 -0800 Subject: [PATCH 03/72] added new methods to README and module doctstrings --- README.md | 5 +++++ src/bst.py | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/README.md b/README.md index d4d8ec5..e5337ab 100755 --- a/README.md +++ b/README.md @@ -30,3 +30,8 @@ Methods include: Trees that are higher on the left than the right should return a positive value; trees that are higher on the right than the left should return a negative value; an ideally-balanced tree should return 0. +* in_order(self): Return a generator that returns each node value from in-order traversal. +* pre_order(self): Return a generator that returns each node value from pre-order traversal. +* post_order(self): Return a generator that returns each node value from post_order traversal. +* breadth_first(self): Return a generator returns each node value from breadth-first traversal. + diff --git a/src/bst.py b/src/bst.py index 1e2f3cc..b08ebb8 100644 --- a/src/bst.py +++ b/src/bst.py @@ -10,6 +10,11 @@ Trees that are higher on the left than the right should return a positive value; trees that are higher on the right than the left should return a negative value; an ideally-balanced tree should return 0. +in_order(self): Return a generator that returns each node value from in-order traversal. +pre_order(self): Return a generator that returns each node value from pre-order traversal. +post_order(self): Return a generator that returns each node value from post_order traversal. +breadth_first(self): Return a generator returns each node value from breadth-first traversal. + """ from queue import Queue From cb54d06aac08daeb6f0b88eb251ece7352930747 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Wed, 18 Jan 2017 14:28:22 -0800 Subject: [PATCH 04/72] adding delete method --- src/bst.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/bst.py b/src/bst.py index b08ebb8..2dd6974 100644 --- a/src/bst.py +++ b/src/bst.py @@ -23,11 +23,12 @@ class Node(): """Node object for the binary search tree.""" - def __init__(self, value, left=None, right=None): + def __init__(self, value, left=None, right=None, parent=None): """Instantiate a node object.""" self.value = value self.left = left self.right = right + self.parent = parent class BinarySearchTree(): @@ -54,6 +55,7 @@ def insert(self, value): current_node = current_node.left else: current_node.left = Node(value) + current_node.left.parent = current_node self._size += 1 break elif value > current_node.value: @@ -61,6 +63,7 @@ def insert(self, value): current_node = current_node.right else: current_node.right = Node(value) + current_node.right.parent = current_node self._size += 1 break else: @@ -186,6 +189,7 @@ def post_order(self): yield peek_node last_node_vis = stack.pop() + def breadth_first(self): """Return a generator that yields tree values according to breadth first traversal.""" trav_list = Queue([self.root]) @@ -196,3 +200,14 @@ def breadth_first(self): if current_node.right: trav_list.enqueue(current_node.right) yield current_node + + def delete(self, value): + """Get rid of a node. Or at least its connection.""" + target_node = self.search(value) + if not (target_node.left or target_node.right): + if target_node.value > target_node.parent.value: + target_node.parent.right = None + target_node.parent = None + else: + target_node.parent.left = None + target_node.parent = None From 18d787744787f407352aa87759568838721d2f03 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Wed, 18 Jan 2017 14:53:33 -0800 Subject: [PATCH 05/72] added 1-child node instances for delete --- src/bst.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/bst.py b/src/bst.py index 2dd6974..18641b1 100644 --- a/src/bst.py +++ b/src/bst.py @@ -204,10 +204,35 @@ def breadth_first(self): def delete(self, value): """Get rid of a node. Or at least its connection.""" target_node = self.search(value) + if not target_node: + return None if not (target_node.left or target_node.right): if target_node.value > target_node.parent.value: target_node.parent.right = None target_node.parent = None + self._size -= 1 else: target_node.parent.left = None target_node.parent = None + self._size -= 1 + elif not (target_node.left and target_node.right): + if target_node.left: + if target_node < target_node.parent: + target_node.left.parent = target_node.parent + target_node.parent.left = target_node.left + target_node.parent = None + target_node.left = None + self._size -= 1 + if target_node.right: + if target_node < target_node.parent: + target_node.right.parent = target_node.parent + target_node.parent.right = target_node.right + target_node.parent = None + target_node.right = None + self._size -= 1 + + + + + target_node.left.parent = target_node.parent.left + From 4ba611ab8092ed28d35301d08b3e884686e5aafc Mon Sep 17 00:00:00 2001 From: Julien Date: Wed, 18 Jan 2017 15:56:16 -0800 Subject: [PATCH 06/72] delete for node with two childs --- src/bst.py | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/bst.py b/src/bst.py index 18641b1..79f5e76 100644 --- a/src/bst.py +++ b/src/bst.py @@ -189,7 +189,6 @@ def post_order(self): yield peek_node last_node_vis = stack.pop() - def breadth_first(self): """Return a generator that yields tree values according to breadth first traversal.""" trav_list = Queue([self.root]) @@ -220,19 +219,37 @@ def delete(self, value): if target_node < target_node.parent: target_node.left.parent = target_node.parent target_node.parent.left = target_node.left - target_node.parent = None - target_node.left = None - self._size -= 1 + else: + target_node.left.parent = target_node.parent + target_node.parent.right = target_node.left + self._size -= 1 + target_node.parent = None + target_node.left = None if target_node.right: if target_node < target_node.parent: + target_node.right.parent = target_node.parent + target_node.parent.left = target_node.right + else: target_node.right.parent = target_node.parent target_node.parent.right = target_node.right - target_node.parent = None - target_node.right = None - self._size -= 1 - - - - - target_node.left.parent = target_node.parent.left - + self._size -= 1 + target_node.parent = None + target_node.right = None + else: + del_node = self.search(value) + current_node = del_node.right + while current_node.left: + current_node = current_node.left + replace_node = current_node + self.delete(current_node) + if del_node.parent: + replace_node.parent = del_node.parent + if replace_node.value < del_node.value: + del_node.parent.left = replace_node + else: + del_node.parent.right = replace_node + replace_node.left = del_node.left + replace_node.right = del_node.right + del_node.parent = None + del_node.left = None + del_node.right = None From cf9a522d55fa125e77d99609c568e7596c7ef5fc Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Wed, 18 Jan 2017 15:56:36 -0800 Subject: [PATCH 07/72] added the easy tests for delete --- src/test_bst.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/test_bst.py b/src/test_bst.py index 9572c27..447be16 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -352,3 +352,24 @@ def test_bfs_weird_tree(weird_tree): for node in weird_tree.breadth_first(): bfs_list.append(node.value) assert bfs_list == [50, 44, 79, 2, 48, 80, 49, 83, 90, 100, 103, 102] + + +def test_delete_node_with_no_children(small_tree): + """Test calling delete on node with no children.""" + small_tree.delete(35) + assert small_tree.search(35) == None + + +def test_delete_node_with_no_children_annuls_parent_connection(small_tree): + """Test calling delete on node with no children kills parent's connection.""" + small_tree.delete(35) + assert small_tree.search(40).left is None + + +def test_delete_node_with_one_child_reassigns_connection(small_tree): + """Test deleting a node reassigns its one child to expected new parent.""" + small_tree.delete(40) + assert small_tree.search(35).parent.value == 50 + assert small_tree.search(50).left.value == 35 + +# def test_delete_node_w_ \ No newline at end of file From 1505ae8d057e3ea75b45753ad251dfe76ed952cc Mon Sep 17 00:00:00 2001 From: Julien Date: Wed, 18 Jan 2017 15:58:51 -0800 Subject: [PATCH 08/72] fix in the delete method --- src/bst.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bst.py b/src/bst.py index 79f5e76..a91f04f 100644 --- a/src/bst.py +++ b/src/bst.py @@ -226,7 +226,7 @@ def delete(self, value): target_node.parent = None target_node.left = None if target_node.right: - if target_node < target_node.parent: + if target_node.value < target_node.parent.value: target_node.right.parent = target_node.parent target_node.parent.left = target_node.right else: From 136cd502d1ba10d8d71b86b4f738c720202540c5 Mon Sep 17 00:00:00 2001 From: Julien Date: Wed, 18 Jan 2017 16:09:33 -0800 Subject: [PATCH 09/72] delete fix --- src/bst.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bst.py b/src/bst.py index a91f04f..b561815 100644 --- a/src/bst.py +++ b/src/bst.py @@ -216,7 +216,7 @@ def delete(self, value): self._size -= 1 elif not (target_node.left and target_node.right): if target_node.left: - if target_node < target_node.parent: + if target_node.value < target_node.parent.value: target_node.left.parent = target_node.parent target_node.parent.left = target_node.left else: From 2214ff2f93128b7cdd49949fd475db6c4986d706 Mon Sep 17 00:00:00 2001 From: Julien Date: Wed, 18 Jan 2017 16:22:38 -0800 Subject: [PATCH 10/72] fix in delete, more tests --- src/bst.py | 2 +- src/test_bst.py | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/bst.py b/src/bst.py index b561815..0e4852e 100644 --- a/src/bst.py +++ b/src/bst.py @@ -241,7 +241,7 @@ def delete(self, value): while current_node.left: current_node = current_node.left replace_node = current_node - self.delete(current_node) + self.delete(current_node.value) if del_node.parent: replace_node.parent = del_node.parent if replace_node.value < del_node.value: diff --git a/src/test_bst.py b/src/test_bst.py index 447be16..456fb51 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -360,6 +360,12 @@ def test_delete_node_with_no_children(small_tree): assert small_tree.search(35) == None +def test_delete_node_with_no_children_update_size(small_tree): + """Test calling delete on node with no children.""" + small_tree.delete(35) + assert small_tree.size() == 5 + + def test_delete_node_with_no_children_annuls_parent_connection(small_tree): """Test calling delete on node with no children kills parent's connection.""" small_tree.delete(35) @@ -372,4 +378,8 @@ def test_delete_node_with_one_child_reassigns_connection(small_tree): assert small_tree.search(35).parent.value == 50 assert small_tree.search(50).left.value == 35 -# def test_delete_node_w_ \ No newline at end of file + +def test_delete_node_with_two_childs_updates_size(small_tree): + """Test that delete node with two childs updates size.""" + small_tree.delete(80) + assert small_tree.size() == 5 From dba1e8561376e082c652fa3590926a3ea51f6265 Mon Sep 17 00:00:00 2001 From: Julien Date: Thu, 19 Jan 2017 13:25:59 -0800 Subject: [PATCH 11/72] balance fix --- src/bst.py | 59 +++++++++++++++++++----------------------------------- 1 file changed, 21 insertions(+), 38 deletions(-) diff --git a/src/bst.py b/src/bst.py index 0e4852e..9151a1f 100644 --- a/src/bst.py +++ b/src/bst.py @@ -134,11 +134,11 @@ def contains(self, value): def balance(self): """Return numerical representation of how balanced the tree is.""" if self.root.left: - depth_left = self.depth(self.root.left) + depth_left = self.depth(self.root.left) + 1 else: depth_left = 0 if self.root.right: - depth_right = self.depth(self.root.right) + depth_right = self.depth(self.root.right) + 1 else: depth_right = 0 balance = depth_right - depth_left @@ -204,52 +204,35 @@ def delete(self, value): """Get rid of a node. Or at least its connection.""" target_node = self.search(value) if not target_node: - return None + return if not (target_node.left or target_node.right): if target_node.value > target_node.parent.value: target_node.parent.right = None - target_node.parent = None - self._size -= 1 else: target_node.parent.left = None - target_node.parent = None - self._size -= 1 elif not (target_node.left and target_node.right): if target_node.left: - if target_node.value < target_node.parent.value: - target_node.left.parent = target_node.parent - target_node.parent.left = target_node.left - else: - target_node.left.parent = target_node.parent - target_node.parent.right = target_node.left - self._size -= 1 - target_node.parent = None - target_node.left = None - if target_node.right: - if target_node.value < target_node.parent.value: - target_node.right.parent = target_node.parent - target_node.parent.left = target_node.right - else: - target_node.right.parent = target_node.parent - target_node.parent.right = target_node.right - self._size -= 1 - target_node.parent = None - target_node.right = None + target_node.left.parent = target_node.parent + target_node.parent.left = target_node.left + else: + target_node.right.parent = target_node.parent + target_node.parent.right = target_node.right else: - del_node = self.search(value) - current_node = del_node.right + current_node = target_node.right while current_node.left: current_node = current_node.left replace_node = current_node self.delete(current_node.value) - if del_node.parent: - replace_node.parent = del_node.parent - if replace_node.value < del_node.value: - del_node.parent.left = replace_node + self._size += 1 # undoes size change within delete + if target_node.parent: + replace_node.parent = target_node.parent + if replace_node.value < target_node.value: + target_node.parent.left = replace_node else: - del_node.parent.right = replace_node - replace_node.left = del_node.left - replace_node.right = del_node.right - del_node.parent = None - del_node.left = None - del_node.right = None + target_node.parent.right = replace_node + replace_node.left = target_node.left + replace_node.right = target_node.right + target_node.parent = None + target_node.left = None + target_node.right = None + self._size -= 1 From 621f7ff93cf664847ed41cc5a0f377c15e5a7de4 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Thu, 19 Jan 2017 13:26:43 -0800 Subject: [PATCH 12/72] added tests for delete to check two-way connections of deleted; added test for contains --- src/test_bst.py | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/test_bst.py b/src/test_bst.py index 447be16..f511f92 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -194,6 +194,11 @@ def test_contains_true_weird_tree_root(weird_tree): assert weird_tree.contains(50) is True +def test_contains_with_nonexistent_val_gt_root(small_tree): + """Test contains returns False when value is greater than root but node nonexistent.""" + assert small_tree.contains(99) is False + + def test_depth_on_small_tree(small_tree): """Test the size on a small Tree.""" assert small_tree.depth() == 2 @@ -213,6 +218,13 @@ def test_balance_on_weird_tree(weird_tree): """Test balance of smal tree fixture.""" assert weird_tree.balance() == 4 +def test_balance_w_no_left_nodes(): + b_tree = BinarySearchTree() + b_tree.insert(17) + b_tree.insert(43) + import pdb; pdb.set_trace() + assert b_tree.balance() == 1 + def test_inorder_no_nodes(): """Test in-order traversal on empty tree returns empty path.""" @@ -364,12 +376,34 @@ def test_delete_node_with_no_children_annuls_parent_connection(small_tree): """Test calling delete on node with no children kills parent's connection.""" small_tree.delete(35) assert small_tree.search(40).left is None + with pytest.raises(AttributeError): + assert small_tree.search(35).parent -def test_delete_node_with_one_child_reassigns_connection(small_tree): +def test_delete_node_with_no_children_annuls_own_connection(small_tree): + """Test calling delete on node with no children kills parent's connection.""" + small_tree.delete(35) + with pytest.raises(AttributeError): + assert small_tree.search(35).parent + + +def test_delete_node_with_one_child_reassigns_connections(small_tree): """Test deleting a node reassigns its one child to expected new parent.""" small_tree.delete(40) assert small_tree.search(35).parent.value == 50 assert small_tree.search(50).left.value == 35 -# def test_delete_node_w_ \ No newline at end of file + +def test_delete_node_annuls_own_connections(small_tree): + """Test calling delete on node kills parent and child connections.""" + small_tree.delete(40) + with pytest.raises(AttributeError): + assert small_tree.search(40).parent is None + with pytest.raises(AttributeError): + assert small_tree.search(40).left is None + + +def test_delete_updates_size(small_tree): + """Test that deleting a node updates tree's size.""" + small_tree.delete(40) + assert small_tree.size() == 5 From 95ec63663852ef19ec0cc7c36bb74c3f28f16b13 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Thu, 19 Jan 2017 13:32:27 -0800 Subject: [PATCH 13/72] deleted corpse code --- src/test_bst.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test_bst.py b/src/test_bst.py index b8aaf8f..e9cff62 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -222,7 +222,6 @@ def test_balance_w_no_left_nodes(): b_tree = BinarySearchTree() b_tree.insert(17) b_tree.insert(43) - import pdb; pdb.set_trace() assert b_tree.balance() == 1 From cc7ad6cc8411ffb327c61f41dad44f68e35b6b67 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Thu, 19 Jan 2017 14:11:13 -0800 Subject: [PATCH 14/72] changed balance method so it can take non-root nodes as argument --- src/bst.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/bst.py b/src/bst.py index 9151a1f..0d5be77 100644 --- a/src/bst.py +++ b/src/bst.py @@ -131,14 +131,14 @@ def contains(self, value): else: return False - def balance(self): - """Return numerical representation of how balanced the tree is.""" - if self.root.left: - depth_left = self.depth(self.root.left) + 1 + def balance(self, node=self.root): + """Return numerical representation of how balanced the tree (or branches) is.""" + if node.left: + depth_left = self.depth(node.left) + 1 else: depth_left = 0 - if self.root.right: - depth_right = self.depth(self.root.right) + 1 + if node.right: + depth_right = self.depth(node.right) + 1 else: depth_right = 0 balance = depth_right - depth_left From 5e0c421ca589f89f3190e083055b05e1c12b6fde Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Thu, 19 Jan 2017 14:45:10 -0800 Subject: [PATCH 15/72] added autobalanc helper functions to rotate left/right --- src/bst.py | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/bst.py b/src/bst.py index 0d5be77..5db564d 100644 --- a/src/bst.py +++ b/src/bst.py @@ -131,8 +131,10 @@ def contains(self, value): else: return False - def balance(self, node=self.root): + def balance(self): """Return numerical representation of how balanced the tree (or branches) is.""" + if node is None: + node = self.root if node.left: depth_left = self.depth(node.left) + 1 else: @@ -144,6 +146,42 @@ def balance(self, node=self.root): balance = depth_right - depth_left return balance + + def autobalance(self, node=self.root): + """Make sure tree rebalances itself.""" + nodes = post_order() + this_node = next(nodes) + if abs(balance(this_node)) > 1: + + + def rebalance(self, node): + if balance(node) < -1 and balance(node.left) < 0: + + + + def rotate_right(self, node, holder_node=None): + """Helper function to shift nodes clockwise.""" + if node.left.right: + holder_node = node.left.right + node.left.right = node + node.parent = node.left + node.left.parent = None + node.left = holder_node + node.left.parent = node + + + def rotate_left(self, node, holder_node=None): + """Helper function to shift nodes counterclockwise.""" + if node.left.right: + holder_node = node.right.left + node.right.left = node + node.parent = node.right + node.right.parent = None + node.right = holder_node + node.right.parent = node + + + def in_order(self): """Return generator that returns tree values one at a time using in-order traversal.""" stack = [] From c63dc6d27888c8b5aa98d647ff2e64733f5d0256 Mon Sep 17 00:00:00 2001 From: Julien Date: Thu, 19 Jan 2017 15:16:06 -0800 Subject: [PATCH 16/72] tests for rotation --- src/bst.py | 35 +++++++++++++---------- src/test_bst.py | 74 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 15 deletions(-) diff --git a/src/bst.py b/src/bst.py index 5db564d..53a7830 100644 --- a/src/bst.py +++ b/src/bst.py @@ -131,7 +131,7 @@ def contains(self, value): else: return False - def balance(self): + def balance(self, node=None): """Return numerical representation of how balanced the tree (or branches) is.""" if node is None: node = self.root @@ -146,20 +146,22 @@ def balance(self): balance = depth_right - depth_left return balance - - def autobalance(self, node=self.root): + def autobalance(self, node=None): """Make sure tree rebalances itself.""" - nodes = post_order() + if node is None: + node = self.root + nodes = self.post_order() this_node = next(nodes) - if abs(balance(this_node)) > 1: - + if abs(self.balance(this_node)) > 1: + pass def rebalance(self, node): - if balance(node) < -1 and balance(node.left) < 0: - + """Balance the given node.""" + if self.balance(node) < -1 and self.balance(node.left) < 0: + pass - - def rotate_right(self, node, holder_node=None): + # deleting 35 but no rotating anything + def rotate_right(self, node, holder_node=None): """Helper function to shift nodes clockwise.""" if node.left.right: holder_node = node.left.right @@ -167,8 +169,10 @@ def rotate_right(self, node, holder_node=None): node.parent = node.left node.left.parent = None node.left = holder_node - node.left.parent = node - + if holder_node: + node.left.parent = node + if node == self.root: + self.root = node.parent def rotate_left(self, node, holder_node=None): """Helper function to shift nodes counterclockwise.""" @@ -178,9 +182,10 @@ def rotate_left(self, node, holder_node=None): node.parent = node.right node.right.parent = None node.right = holder_node - node.right.parent = node - - + if holder_node: + node.right.parent = node + if node == self.root: + self.root = node.parent def in_order(self): """Return generator that returns tree values one at a time using in-order traversal.""" diff --git a/src/test_bst.py b/src/test_bst.py index e9cff62..ce02b82 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -419,3 +419,77 @@ def test_delete_node_with_two_childs_updates_size(small_tree): """Test that delete node with two childs updates size.""" small_tree.delete(80) assert small_tree.size() == 5 + + +def test_rotate_left_small_tree_assign_child(small_tree): + """Test that left rotation on small tree reassigns children.""" + small_tree.rotate_left(small_tree.root) + assert small_tree.search(80).left.value == 50 + + +def test_rotate_left_small_tree_assign_parent(small_tree): + """Test that left rotation on small tree reassigns parent.""" + small_tree.rotate_left(small_tree.root) + assert small_tree.search(50).parent.value == 80 + + +def test_rotate_left_reassigns_root(small_tree): + """Test the left rotation reassigns root.""" + small_tree.rotate_left(small_tree.root) + assert small_tree.root.value == 80 + + +def test_rotate_left_doesnt_reassign_root(small_tree): + """Test the left rotation does not reassign root.""" + small_tree.rotate_left(small_tree.search(80)) + assert small_tree.root.value == 50 + + +def test_rotate_left_small_tree_assign_parent_not_root(small_tree): + """Test that left rotation on small tree reassigns children.""" + small_tree.rotate_left(small_tree.search(80)) + assert small_tree.search(80).parent.value == 90 + + +def test_rotate_left_small_tree_assign_child_not_root(small_tree): + """Test that left rotation on small tree reassigns children.""" + small_tree.rotate_left(small_tree.search(80)) + assert small_tree.search(80).right is None + + +def test_rotate_right_small_tree_assign_child(small_tree): + """Test that right rotation on small tree reassigns children.""" + small_tree.rotate_right(small_tree.root) + assert small_tree.search(40).right.value == 50 + + +def test_rotate_right_small_tree_assign_parent(small_tree): + """Test that right rotation on small tree reassigns parent.""" + small_tree.rotate_right(small_tree.root) + assert small_tree.search(50).parent.value == 40 + + +def test_rotate_right_small_tree_assign_parent_not_root(small_tree): + """Test that right rotation on small tree reassigns children.""" + small_tree.rotate_right(small_tree.search(40)) + assert small_tree.search(40).parent.value == 35 + + +def test_rotate_right_small_tree_assign_child_not_root(small_tree): + """Test that right rotation on small tree reassigns children.""" + small_tree.rotate_right(small_tree.search(40)) + assert small_tree.search(40).left is None + + +def test_rotate_right_small_tree_assign_parent_child_not_root(small_tree): + """Test that right rotation on small tree reassigns children.""" + small_tree.rotate_right(small_tree.search(40)) + import pdb; pdb.set_trace() # deleting 35 but no rotating anything + assert small_tree.search(50).left.value == 35 + + +def test_rotate_right_small_tree_assign_right_child_not_root(small_tree): + """Test that right rotation on small tree reassigns children.""" + small_tree.rotate_right(small_tree.search(40)) + import pdb; pdb.set_trace() # deleting 35 but no rotating anything + assert small_tree.search(35).right is None From 98ec9d159beef8df5b17541ab24f0185d8f6bc68 Mon Sep 17 00:00:00 2001 From: Julien Date: Mon, 23 Jan 2017 16:47:17 -0800 Subject: [PATCH 17/72] bug in left right rotation --- src/bst.py | 69 +++++++++++++++++++++++++++++++++++++------------ src/test_bst.py | 14 +++++++--- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/bst.py b/src/bst.py index 53a7830..4de75a9 100644 --- a/src/bst.py +++ b/src/bst.py @@ -68,6 +68,7 @@ def insert(self, value): break else: break + self.autobalance() def search(self, value): """Search the Binary Search Tree for a value, return node or none.""" @@ -131,9 +132,11 @@ def contains(self, value): else: return False - def balance(self, node=None): + def balance(self, node='root'): """Return numerical representation of how balanced the tree (or branches) is.""" if node is None: + return 0 + if node == 'root': node = self.root if node.left: depth_left = self.depth(node.left) + 1 @@ -148,26 +151,51 @@ def balance(self, node=None): def autobalance(self, node=None): """Make sure tree rebalances itself.""" + # import pdb; pdb.set_trace() if node is None: node = self.root nodes = self.post_order() - this_node = next(nodes) - if abs(self.balance(this_node)) > 1: - pass + while True: + try: + this_node = next(nodes) + except StopIteration: + break + if abs(self.balance(this_node)) > 1: + self.rebalance(this_node) + # pass def rebalance(self, node): """Balance the given node.""" - if self.balance(node) < -1 and self.balance(node.left) < 0: - pass + if self.balance(node) > 1: + if self.balance(node.right) >= 1: + self.rotate_left(node) + else: + self.rotate_right(node.right) + self.rotate_left(node) + elif self.balance(node) < 1: + if self.balance(node.left) >= 1: + self.rotate_right(node) + else: + self.rotate_left(node.left) + self.rotate_right(node) - # deleting 35 but no rotating anything - def rotate_right(self, node, holder_node=None): + + # deleting 35 but no rotating anything + def rotate_right(self, node, holder_node=None): """Helper function to shift nodes clockwise.""" - if node.left.right: - holder_node = node.left.right - node.left.right = node + if node is None: + return + try: + if node.left.right: + holder_node = node.left.right + except AttributeError: + pass + if node.left: + node.left.parent = node.parent + node.left.right = node + if node.parent: + node.parent.left = node.left node.parent = node.left - node.left.parent = None node.left = holder_node if holder_node: node.left.parent = node @@ -176,11 +204,20 @@ def rotate_right(self, node, holder_node=None): def rotate_left(self, node, holder_node=None): """Helper function to shift nodes counterclockwise.""" - if node.left.right: - holder_node = node.right.left - node.right.left = node + if node is None: + return + try: + if node.right.left: + holder_node = node.right.left + except AttributeError: + pass + + if node.right: + node.right.parent = node.parent + node.right.left = node + if node.parent: + node.parent.right = node.right node.parent = node.right - node.right.parent = None node.right = holder_node if holder_node: node.right.parent = node diff --git a/src/test_bst.py b/src/test_bst.py index ce02b82..25a4705 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -21,6 +21,8 @@ def small_tree(): @pytest.fixture() def weird_tree(): """Grow a small tree with five nodes.""" + import pdb; pdb.set_trace() + tree = BinarySearchTree() tree.insert(50) tree.insert(79) @@ -218,6 +220,7 @@ def test_balance_on_weird_tree(weird_tree): """Test balance of smal tree fixture.""" assert weird_tree.balance() == 4 + def test_balance_w_no_left_nodes(): b_tree = BinarySearchTree() b_tree.insert(17) @@ -367,6 +370,7 @@ def test_bfs_weird_tree(weird_tree): def test_delete_node_with_no_children(small_tree): """Test calling delete on node with no children.""" + import pdb; pdb.set_trace() small_tree.delete(35) assert small_tree.search(35) == None @@ -412,11 +416,12 @@ def test_delete_node_annuls_own_connections(small_tree): def test_delete_updates_size(small_tree): """Test that deleting a node updates tree's size.""" small_tree.delete(40) - assert small_tree.size() == 5 + assert small_tree.size() == 5 def test_delete_node_with_two_childs_updates_size(small_tree): """Test that delete node with two childs updates size.""" + import pdb; pdb.set_trace() small_tree.delete(80) assert small_tree.size() == 5 @@ -483,13 +488,14 @@ def test_rotate_right_small_tree_assign_child_not_root(small_tree): def test_rotate_right_small_tree_assign_parent_child_not_root(small_tree): """Test that right rotation on small tree reassigns children.""" + import pdb; pdb.set_trace() small_tree.rotate_right(small_tree.search(40)) - import pdb; pdb.set_trace() # deleting 35 but no rotating anything + # import pdb; pdb.set_trace() # deleting 35 but no rotating anything assert small_tree.search(50).left.value == 35 def test_rotate_right_small_tree_assign_right_child_not_root(small_tree): """Test that right rotation on small tree reassigns children.""" small_tree.rotate_right(small_tree.search(40)) - import pdb; pdb.set_trace() # deleting 35 but no rotating anything - assert small_tree.search(35).right is None + # import pdb; pdb.set_trace() # deleting 35 but no rotating anything + assert small_tree.search(35).left is None From b26a465cbfaef954600986be93bfd81be5e09885 Mon Sep 17 00:00:00 2001 From: Julien Date: Mon, 23 Jan 2017 17:26:22 -0800 Subject: [PATCH 18/72] true/false switch for autobalancing tree --- src/bst.py | 5 +++-- src/test_bst.py | 41 +++++++++++++++++++++-------------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/bst.py b/src/bst.py index 4de75a9..96d8503 100644 --- a/src/bst.py +++ b/src/bst.py @@ -42,7 +42,7 @@ def __init__(self): self._size = 0 self.root = None - def insert(self, value): + def insert(self, value, autobalance=True): """Insert a value in to the binary search tree.""" if self.root is None: self.root = Node(value) @@ -68,7 +68,8 @@ def insert(self, value): break else: break - self.autobalance() + if autobalance: + self.autobalance() def search(self, value): """Search the Binary Search Tree for a value, return node or none.""" diff --git a/src/test_bst.py b/src/test_bst.py index 25a4705..7040cbd 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -21,21 +21,21 @@ def small_tree(): @pytest.fixture() def weird_tree(): """Grow a small tree with five nodes.""" - import pdb; pdb.set_trace() + # import pdb; pdb.set_trace() tree = BinarySearchTree() - tree.insert(50) - tree.insert(79) - tree.insert(80) - tree.insert(83) - tree.insert(90) - tree.insert(100) - tree.insert(44) - tree.insert(48) - tree.insert(49) - tree.insert(103) - tree.insert(2) - tree.insert(102) + tree.insert(50, autobalance=False) + tree.insert(79, autobalance=False) + tree.insert(80, autobalance=False) + tree.insert(83, autobalance=False) + tree.insert(90, autobalance=False) + tree.insert(100, autobalance=False) + tree.insert(44, autobalance=False) + tree.insert(48, autobalance=False) + tree.insert(49, autobalance=False) + tree.insert(103, autobalance=False) + tree.insert(2, autobalance=False) + tree.insert(102, autobalance=False) return tree @@ -110,25 +110,26 @@ def test_inserting_higher_val_pushes_right(): def test_inserting_less_but_more_into_populated_tree(small_tree): """Test inserting lower value that would push left then right.""" - small_tree.insert(43) + small_tree.insert(43, autobalance=False) assert small_tree.root.left.right.value == 43 def test_inserting_lower_item_into_populated_tree(small_tree): """Test inserting value that pushes all the way left.""" - small_tree.insert(33) + small_tree.insert(33, autobalance=False) + import pdb; pdb.set_trace() assert small_tree.root.left.left.left.value == 33 def test_insert_to_small_tree_updates_size(small_tree): """Test that insert on small tree increments size.""" - small_tree.insert(43) + small_tree.insert(43, autobalance=False) assert small_tree._size == 7 def test_insert_to_small_tree_existing_num(small_tree): """Test that inserting existing number doesn't change size.""" - small_tree.insert(40) + small_tree.insert(40, autobalance=False) assert small_tree.size() == 6 @@ -370,7 +371,7 @@ def test_bfs_weird_tree(weird_tree): def test_delete_node_with_no_children(small_tree): """Test calling delete on node with no children.""" - import pdb; pdb.set_trace() + # import pdb; pdb.set_trace() small_tree.delete(35) assert small_tree.search(35) == None @@ -421,7 +422,7 @@ def test_delete_updates_size(small_tree): def test_delete_node_with_two_childs_updates_size(small_tree): """Test that delete node with two childs updates size.""" - import pdb; pdb.set_trace() + # import pdb; pdb.set_trace() small_tree.delete(80) assert small_tree.size() == 5 @@ -488,7 +489,7 @@ def test_rotate_right_small_tree_assign_child_not_root(small_tree): def test_rotate_right_small_tree_assign_parent_child_not_root(small_tree): """Test that right rotation on small tree reassigns children.""" - import pdb; pdb.set_trace() + # import pdb; pdb.set_trace() small_tree.rotate_right(small_tree.search(40)) # import pdb; pdb.set_trace() # deleting 35 but no rotating anything assert small_tree.search(50).left.value == 35 From 99a354c4755e03ed47c304f4cdc599dbcfb7b262 Mon Sep 17 00:00:00 2001 From: Julien Date: Tue, 24 Jan 2017 13:46:04 -0800 Subject: [PATCH 19/72] fixed the rotations bug. small operator error. --- src/bst.py | 7 ++----- src/test_bst.py | 25 ++++++++++++++++--------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/bst.py b/src/bst.py index 96d8503..a88799a 100644 --- a/src/bst.py +++ b/src/bst.py @@ -152,7 +152,6 @@ def balance(self, node='root'): def autobalance(self, node=None): """Make sure tree rebalances itself.""" - # import pdb; pdb.set_trace() if node is None: node = self.root nodes = self.post_order() @@ -173,15 +172,13 @@ def rebalance(self, node): else: self.rotate_right(node.right) self.rotate_left(node) - elif self.balance(node) < 1: - if self.balance(node.left) >= 1: + elif self.balance(node) < -1: + if self.balance(node.left) <= -1: self.rotate_right(node) else: self.rotate_left(node.left) self.rotate_right(node) - - # deleting 35 but no rotating anything def rotate_right(self, node, holder_node=None): """Helper function to shift nodes clockwise.""" if node is None: diff --git a/src/test_bst.py b/src/test_bst.py index 7040cbd..dc139ad 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -21,8 +21,6 @@ def small_tree(): @pytest.fixture() def weird_tree(): """Grow a small tree with five nodes.""" - # import pdb; pdb.set_trace() - tree = BinarySearchTree() tree.insert(50, autobalance=False) tree.insert(79, autobalance=False) @@ -117,7 +115,6 @@ def test_inserting_less_but_more_into_populated_tree(small_tree): def test_inserting_lower_item_into_populated_tree(small_tree): """Test inserting value that pushes all the way left.""" small_tree.insert(33, autobalance=False) - import pdb; pdb.set_trace() assert small_tree.root.left.left.left.value == 33 @@ -223,6 +220,7 @@ def test_balance_on_weird_tree(weird_tree): def test_balance_w_no_left_nodes(): + """Test the balance of a tree with only a root and its right child.""" b_tree = BinarySearchTree() b_tree.insert(17) b_tree.insert(43) @@ -371,7 +369,6 @@ def test_bfs_weird_tree(weird_tree): def test_delete_node_with_no_children(small_tree): """Test calling delete on node with no children.""" - # import pdb; pdb.set_trace() small_tree.delete(35) assert small_tree.search(35) == None @@ -404,7 +401,6 @@ def test_delete_node_with_one_child_reassigns_connections(small_tree): assert small_tree.search(50).left.value == 35 - def test_delete_node_annuls_own_connections(small_tree): """Test calling delete on node kills parent and child connections.""" small_tree.delete(40) @@ -422,7 +418,6 @@ def test_delete_updates_size(small_tree): def test_delete_node_with_two_childs_updates_size(small_tree): """Test that delete node with two childs updates size.""" - # import pdb; pdb.set_trace() small_tree.delete(80) assert small_tree.size() == 5 @@ -489,14 +484,26 @@ def test_rotate_right_small_tree_assign_child_not_root(small_tree): def test_rotate_right_small_tree_assign_parent_child_not_root(small_tree): """Test that right rotation on small tree reassigns children.""" - # import pdb; pdb.set_trace() small_tree.rotate_right(small_tree.search(40)) - # import pdb; pdb.set_trace() # deleting 35 but no rotating anything assert small_tree.search(50).left.value == 35 def test_rotate_right_small_tree_assign_right_child_not_root(small_tree): """Test that right rotation on small tree reassigns children.""" small_tree.rotate_right(small_tree.search(40)) - # import pdb; pdb.set_trace() # deleting 35 but no rotating anything assert small_tree.search(35).left is None + + +def test_tree_autobalances(): + """Test that a right skewed tree is balanced after many insertions.""" + tree = BinarySearchTree() + tree.insert(50) + tree.insert(60) + tree.insert(70) + tree.insert(80) + tree.insert(90) + tree.insert(100) + tree.insert(66) + tree.insert(59) + tree.insert(89) + assert abs(tree.balance()) <= 1 From 61c5e7d2a85d724748c4e24e585275dab9333861 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Tue, 24 Jan 2017 13:48:19 -0800 Subject: [PATCH 20/72] adding to docstrings --- src/bst.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/bst.py b/src/bst.py index 4de75a9..a5af578 100644 --- a/src/bst.py +++ b/src/bst.py @@ -1,19 +1,20 @@ """Classes for binary search tree. Methods include: -insert(self, val): Insert value into tree; if value already exists, ignore it. -search(self, val): Return node containing that value, else None. -size(self): Return number of nodes/vertices in tree, 0 if empty. -depth(self): Return number of levels in tree. Tree with one value has depth of 0. -contains(self, val): Return True if value is in tree, False if not. -balance(self): Return a positive or negative integer representing tree's balance. +insert(val): Insert value into tree; if value already exists, ignore it. Method autobalances after insertion. +search(val): Return node containing that value, else None. +size(): Return number of nodes/vertices in tree, 0 if empty. +depth(): Return number of levels in tree. Tree with one value has depth of 0. +contains(val): Return True if value is in tree, False if not. +balance(): Return a positive or negative integer representing tree's balance. Trees that are higher on the left than the right should return a positive value; trees that are higher on the right than the left should return a negative value; an ideally-balanced tree should return 0. -in_order(self): Return a generator that returns each node value from in-order traversal. -pre_order(self): Return a generator that returns each node value from pre-order traversal. -post_order(self): Return a generator that returns each node value from post_order traversal. -breadth_first(self): Return a generator returns each node value from breadth-first traversal. +in_order(): Return a generator that returns each node value from in-order traversal. +pre_order(): Return a generator that returns each node value from pre-order traversal. +post_order(): Return a generator that returns each node value from post_order traversal. +breadth_first(): Return a generator returns each node value from breadth-first traversal. +delete(value): Delete a node's connections (edges), effectively deleting node. """ @@ -151,17 +152,17 @@ def balance(self, node='root'): def autobalance(self, node=None): """Make sure tree rebalances itself.""" - # import pdb; pdb.set_trace() if node is None: node = self.root nodes = self.post_order() while True: try: this_node = next(nodes) + if abs(self.balance(this_node)) > 1: + self.rebalance(this_node) except StopIteration: break - if abs(self.balance(this_node)) > 1: - self.rebalance(this_node) + # pass def rebalance(self, node): From 2f8af436a8271e1a77876030b065993c23274041 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Tue, 24 Jan 2017 14:01:58 -0800 Subject: [PATCH 21/72] added docstrings, made autobalance, rebalance and rotations into hidden methods --- README.md | 5 ++++- src/bst.py | 33 +++++++++++++++++++-------------- src/test_bst.py | 40 ++++++++++++++++++++-------------------- 3 files changed, 43 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 26db65c..578c691 100755 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ A linked list that points in both directions A tree of nodes sorted by values less than and greater than root branching to the left and right, respectively. Methods include: -* insert(self, val): Insert value into tree; if value already exists, ignore it. +* insert(self, val): Insert value into tree; if value already exists, ignore it. Method autobalances after insertion, and tree size increments by one. * search(self, val): Return node containing that value, else None. * size(self): Return number of nodes/vertices in tree, 0 if empty. * depth(self): Return number of levels in tree. Tree with one value has depth of 0. @@ -36,4 +36,7 @@ Methods include: * pre_order(self): Return a generator that returns each node value from pre-order traversal. * post_order(self): Return a generator that returns each node value from post_order traversal. * breadth_first(self): Return a generator returns each node value from breadth-first traversal. +* delete(value): Delete a node's connections (edges), effectively deleting node. Method autobalances after deletion, and tree size decrements by one. + + diff --git a/src/bst.py b/src/bst.py index 7a5ea1e..c87e934 100644 --- a/src/bst.py +++ b/src/bst.py @@ -1,7 +1,8 @@ """Classes for binary search tree. Methods include: -insert(val): Insert value into tree; if value already exists, ignore it. Method autobalances after insertion. +insert(val): Insert value into tree; if value already exists, ignore it. + Method autobalances after insertion, and tree size increments by one. search(val): Return node containing that value, else None. size(): Return number of nodes/vertices in tree, 0 if empty. depth(): Return number of levels in tree. Tree with one value has depth of 0. @@ -15,6 +16,7 @@ post_order(): Return a generator that returns each node value from post_order traversal. breadth_first(): Return a generator returns each node value from breadth-first traversal. delete(value): Delete a node's connections (edges), effectively deleting node. + Method autobalances after deletion, and tree size decrements by one. """ @@ -70,7 +72,7 @@ def insert(self, value, autobalance=True): else: break if autobalance: - self.autobalance() + self._autobalance() def search(self, value): """Search the Binary Search Tree for a value, return node or none.""" @@ -151,7 +153,7 @@ def balance(self, node='root'): balance = depth_right - depth_left return balance - def autobalance(self, node=None): + def _autobalance(self, node=None): """Make sure tree rebalances itself.""" if node is None: node = self.root @@ -160,28 +162,28 @@ def autobalance(self, node=None): try: this_node = next(nodes) if abs(self.balance(this_node)) > 1: - self.rebalance(this_node) + self._rebalance(this_node) except StopIteration: break # pass - def rebalance(self, node): + def _rebalance(self, node): """Balance the given node.""" if self.balance(node) > 1: if self.balance(node.right) >= 1: - self.rotate_left(node) + self._rotate_left(node) else: - self.rotate_right(node.right) - self.rotate_left(node) + self._rotate_right(node.right) + self._rotate_left(node) elif self.balance(node) < -1: if self.balance(node.left) <= -1: - self.rotate_right(node) + self._rotate_right(node) else: - self.rotate_left(node.left) - self.rotate_right(node) + self._rotate_left(node.left) + self._rotate_right(node) - def rotate_right(self, node, holder_node=None): + def _rotate_right(self, node, holder_node=None): """Helper function to shift nodes clockwise.""" if node is None: return @@ -202,7 +204,7 @@ def rotate_right(self, node, holder_node=None): if node == self.root: self.root = node.parent - def rotate_left(self, node, holder_node=None): + def _rotate_left(self, node, holder_node=None): """Helper function to shift nodes counterclockwise.""" if node is None: return @@ -280,7 +282,7 @@ def breadth_first(self): trav_list.enqueue(current_node.right) yield current_node - def delete(self, value): + def delete(self, value, autobalance=True): """Get rid of a node. Or at least its connection.""" target_node = self.search(value) if not target_node: @@ -316,3 +318,6 @@ def delete(self, value): target_node.left = None target_node.right = None self._size -= 1 + if autobalance: + self._autobalance() + diff --git a/src/test_bst.py b/src/test_bst.py index dc139ad..1aa7243 100644 --- a/src/test_bst.py +++ b/src/test_bst.py @@ -369,19 +369,19 @@ def test_bfs_weird_tree(weird_tree): def test_delete_node_with_no_children(small_tree): """Test calling delete on node with no children.""" - small_tree.delete(35) + small_tree.delete(35, autobalance=False) assert small_tree.search(35) == None def test_delete_node_with_no_children_update_size(small_tree): """Test calling delete on node with no children.""" - small_tree.delete(35) + small_tree.delete(35, autobalance=False) assert small_tree.size() == 5 def test_delete_node_with_no_children_annuls_parent_connection(small_tree): """Test calling delete on node with no children kills parent's connection.""" - small_tree.delete(35) + small_tree.delete(35, autobalance=False) assert small_tree.search(40).left is None with pytest.raises(AttributeError): assert small_tree.search(35).parent @@ -389,21 +389,21 @@ def test_delete_node_with_no_children_annuls_parent_connection(small_tree): def test_delete_node_with_no_children_annuls_own_connection(small_tree): """Test calling delete on node with no children kills parent's connection.""" - small_tree.delete(35) + small_tree.delete(35, autobalance=False) with pytest.raises(AttributeError): assert small_tree.search(35).parent def test_delete_node_with_one_child_reassigns_connections(small_tree): """Test deleting a node reassigns its one child to expected new parent.""" - small_tree.delete(40) + small_tree.delete(40, autobalance=False) assert small_tree.search(35).parent.value == 50 assert small_tree.search(50).left.value == 35 def test_delete_node_annuls_own_connections(small_tree): """Test calling delete on node kills parent and child connections.""" - small_tree.delete(40) + small_tree.delete(40, autobalance=False) with pytest.raises(AttributeError): assert small_tree.search(40).parent is None with pytest.raises(AttributeError): @@ -412,85 +412,85 @@ def test_delete_node_annuls_own_connections(small_tree): def test_delete_updates_size(small_tree): """Test that deleting a node updates tree's size.""" - small_tree.delete(40) + small_tree.delete(40, autobalance=False) assert small_tree.size() == 5 def test_delete_node_with_two_childs_updates_size(small_tree): """Test that delete node with two childs updates size.""" - small_tree.delete(80) + small_tree.delete(80, autobalance=False) assert small_tree.size() == 5 def test_rotate_left_small_tree_assign_child(small_tree): """Test that left rotation on small tree reassigns children.""" - small_tree.rotate_left(small_tree.root) + small_tree._rotate_left(small_tree.root) assert small_tree.search(80).left.value == 50 def test_rotate_left_small_tree_assign_parent(small_tree): """Test that left rotation on small tree reassigns parent.""" - small_tree.rotate_left(small_tree.root) + small_tree._rotate_left(small_tree.root) assert small_tree.search(50).parent.value == 80 def test_rotate_left_reassigns_root(small_tree): """Test the left rotation reassigns root.""" - small_tree.rotate_left(small_tree.root) + small_tree._rotate_left(small_tree.root) assert small_tree.root.value == 80 def test_rotate_left_doesnt_reassign_root(small_tree): """Test the left rotation does not reassign root.""" - small_tree.rotate_left(small_tree.search(80)) + small_tree._rotate_left(small_tree.search(80)) assert small_tree.root.value == 50 def test_rotate_left_small_tree_assign_parent_not_root(small_tree): """Test that left rotation on small tree reassigns children.""" - small_tree.rotate_left(small_tree.search(80)) + small_tree._rotate_left(small_tree.search(80)) assert small_tree.search(80).parent.value == 90 def test_rotate_left_small_tree_assign_child_not_root(small_tree): """Test that left rotation on small tree reassigns children.""" - small_tree.rotate_left(small_tree.search(80)) + small_tree._rotate_left(small_tree.search(80)) assert small_tree.search(80).right is None def test_rotate_right_small_tree_assign_child(small_tree): """Test that right rotation on small tree reassigns children.""" - small_tree.rotate_right(small_tree.root) + small_tree._rotate_right(small_tree.root) assert small_tree.search(40).right.value == 50 def test_rotate_right_small_tree_assign_parent(small_tree): """Test that right rotation on small tree reassigns parent.""" - small_tree.rotate_right(small_tree.root) + small_tree._rotate_right(small_tree.root) assert small_tree.search(50).parent.value == 40 def test_rotate_right_small_tree_assign_parent_not_root(small_tree): """Test that right rotation on small tree reassigns children.""" - small_tree.rotate_right(small_tree.search(40)) + small_tree._rotate_right(small_tree.search(40)) assert small_tree.search(40).parent.value == 35 def test_rotate_right_small_tree_assign_child_not_root(small_tree): """Test that right rotation on small tree reassigns children.""" - small_tree.rotate_right(small_tree.search(40)) + small_tree._rotate_right(small_tree.search(40)) assert small_tree.search(40).left is None def test_rotate_right_small_tree_assign_parent_child_not_root(small_tree): """Test that right rotation on small tree reassigns children.""" - small_tree.rotate_right(small_tree.search(40)) + small_tree._rotate_right(small_tree.search(40)) assert small_tree.search(50).left.value == 35 def test_rotate_right_small_tree_assign_right_child_not_root(small_tree): """Test that right rotation on small tree reassigns children.""" - small_tree.rotate_right(small_tree.search(40)) + small_tree._rotate_right(small_tree.search(40)) assert small_tree.search(35).left is None From ae697bd0e90cff5309a3a365584241bc2a2c167a Mon Sep 17 00:00:00 2001 From: Julien Date: Wed, 25 Jan 2017 08:35:27 -0800 Subject: [PATCH 22/72] hash table --- src/hash_table.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/hash_table.py diff --git a/src/hash_table.py b/src/hash_table.py new file mode 100644 index 0000000..e69de29 From 14197b95503facbaf44b99040c71170b7e82abff Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Wed, 25 Jan 2017 12:34:21 -0800 Subject: [PATCH 23/72] added pseudocode-ish addititve hash --- README.md | 1 - src/hash_table.py | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9ee31d0..0da3d09 100755 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -<<<<<<< HEAD [![Build Status](https://travis-ci.org/julienawilson/data-structures.svg?branch=master)](https://travis-ci.org/julienawilson/data-structures) # Data Structures diff --git a/src/hash_table.py b/src/hash_table.py index e69de29..c575a4f 100644 --- a/src/hash_table.py +++ b/src/hash_table.py @@ -0,0 +1,16 @@ +"""Class for hash tables.""" + +class HashTable(buckets, hash=additive): + """Something something.""" + def __init__(self): + self.bins = [dict for bucket in range(buckets)] + + def _hash(self, additive, word): + self.additive = additive + self.word = word + + additive = sum([ord(char) for char in list(word)]) % len(buckets) + + + + From 366131f44b2115522d142cfc91892fbff1070908 Mon Sep 17 00:00:00 2001 From: Julien Date: Wed, 25 Jan 2017 12:50:25 -0800 Subject: [PATCH 24/72] add hash func written --- src/hash_table.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/hash_table.py b/src/hash_table.py index c575a4f..ac69093 100644 --- a/src/hash_table.py +++ b/src/hash_table.py @@ -1,16 +1,23 @@ """Class for hash tables.""" -class HashTable(buckets, hash=additive): - """Something something.""" - def __init__(self): - self.bins = [dict for bucket in range(buckets)] - def _hash(self, additive, word): - self.additive = additive - self.word = word +class HashTable(object): + """Something something.""" - additive = sum([ord(char) for char in list(word)]) % len(buckets) + def __init__(self, size, hash_alg='additive'): + """Initialize a hash table.""" + self._size = size + self.buckets = [[] for bucket in range(self._size)] + self._hash_alg = hash_alg + # def _hash(self, hash_alg, word): + # if + # self.word = word + # additive = sum([ord(char) for char in list(word)]) % len(buckets) + def _add_hash(self, word): + """Return Additive hash value.""" + return sum([ord(char) for char in list(word)]) % self._size + # def set(self, key, value) \ No newline at end of file From 577725b1fedf72724e99f3d881e26e8f02aebcdf Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Wed, 25 Jan 2017 12:51:39 -0800 Subject: [PATCH 25/72] added blank test file for hash table module --- src/test_hash_table.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/test_hash_table.py diff --git a/src/test_hash_table.py b/src/test_hash_table.py new file mode 100644 index 0000000..28e8635 --- /dev/null +++ b/src/test_hash_table.py @@ -0,0 +1 @@ +"""Test for our implemtation of hash tables.""" From 5ca754b9c11f9310aeb8dc52c43c5e04a3e3bda6 Mon Sep 17 00:00:00 2001 From: Julien Date: Wed, 25 Jan 2017 13:31:45 -0800 Subject: [PATCH 26/72] set and hash functions --- src/hash_table.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/hash_table.py b/src/hash_table.py index ac69093..708d61a 100644 --- a/src/hash_table.py +++ b/src/hash_table.py @@ -8,16 +8,25 @@ def __init__(self, size, hash_alg='additive'): """Initialize a hash table.""" self._size = size self.buckets = [[] for bucket in range(self._size)] - self._hash_alg = hash_alg + self._hash_alg = self._hash(hash_alg) - # def _hash(self, hash_alg, word): - # if - # self.word = word - # additive = sum([ord(char) for char in list(word)]) % len(buckets) + def _hash(self, hash_alg): + if hash_alg == 'additive': + return self._additive_hash + else: + raise ValueError("Please enter a valid hash algorithm. The options are 'additive'.") - def _add_hash(self, word): + def _additive_hash(self, word): """Return Additive hash value.""" return sum([ord(char) for char in list(word)]) % self._size - - # def set(self, key, value) \ No newline at end of file + def set(self, key, value): + """Set a new key value pair in the has table.""" + if type(key) is not str: + raise TypeError("Key for hash table must be a string.") + hash_val = self._hash_alg(key) + for pair in self.buckets[hash_val]: + if pair[0] == key: + pair[1] = value + return + self.buckets[hash_val].append([key, value]) From 1d25cbdcafa32cc324e2f15777eb0473e48c2da8 Mon Sep 17 00:00:00 2001 From: Julien Date: Wed, 25 Jan 2017 13:45:30 -0800 Subject: [PATCH 27/72] get for the hash function --- src/hash_table.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/hash_table.py b/src/hash_table.py index 708d61a..32423bf 100644 --- a/src/hash_table.py +++ b/src/hash_table.py @@ -30,3 +30,13 @@ def set(self, key, value): pair[1] = value return self.buckets[hash_val].append([key, value]) + + def get(self, key): + """Get the value from the hash table.""" + if type(key) is not str: + raise TypeError("Key for hash table must be a string.") + hash_val = self._hash_alg(key) + for pair in self.buckets[hash_val]: + if pair[0] == key: + return pair[1] + return From 0ec88b57bf9d516e808fa3f8f44b4c052362ba3e Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Wed, 25 Jan 2017 13:51:54 -0800 Subject: [PATCH 28/72] added test for seting a number; fix typon in docstrings --- src/hash_table.py | 2 +- src/test_hash_table.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/hash_table.py b/src/hash_table.py index 708d61a..0143662 100644 --- a/src/hash_table.py +++ b/src/hash_table.py @@ -21,7 +21,7 @@ def _additive_hash(self, word): return sum([ord(char) for char in list(word)]) % self._size def set(self, key, value): - """Set a new key value pair in the has table.""" + """Set a new key-value pair in the hash table.""" if type(key) is not str: raise TypeError("Key for hash table must be a string.") hash_val = self._hash_alg(key) diff --git a/src/test_hash_table.py b/src/test_hash_table.py index 28e8635..571db9c 100644 --- a/src/test_hash_table.py +++ b/src/test_hash_table.py @@ -1 +1,16 @@ """Test for our implemtation of hash tables.""" + +import pytest +from hash_table import HashTable + +def test_simple_additive_hash(): + """Test that additive hash on small word.""" + h_table = HashTable(10) + assert h_table._additive_hash('a') == 7 + +def test_set_add_a_nonstring(): + """Test that set() won't take in a number type.""" + h_table = HashTable(10) + with pytest.raises(TypeError): + h_table.set(3, 'AI') + From bf2a18bdad9b152ce0d9a7a38e5b39010dd5e5ad Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Wed, 25 Jan 2017 14:42:01 -0800 Subject: [PATCH 29/72] added tests using unix dictionary list --- src/test_hash_table.py | 54 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/test_hash_table.py b/src/test_hash_table.py index 571db9c..59e892f 100644 --- a/src/test_hash_table.py +++ b/src/test_hash_table.py @@ -1,6 +1,7 @@ """Test for our implemtation of hash tables.""" import pytest +# import os from hash_table import HashTable def test_simple_additive_hash(): @@ -8,9 +9,62 @@ def test_simple_additive_hash(): h_table = HashTable(10) assert h_table._additive_hash('a') == 7 + def test_set_add_a_nonstring(): """Test that set() won't take in a number type.""" h_table = HashTable(10) with pytest.raises(TypeError): h_table.set(3, 'AI') + +def test_set_add_word(): + """Test that set() adds key-value pair.""" + h_table = HashTable(10) + h_table.set('thinking', 'tiring') + assert h_table.get('thinking') == 'tiring' + + +def test_get(): + """Test that get() retrieves value.""" + h_table = HashTable(10) + h_table.set('thinking', 'tiring') + assert h_table.get('thinking') == 'tiring' + +def test_dictionary_attacks_me_test(): + """Dictionary test.""" + h_table = HashTable(3000) + f = open("/usr/share/dict/words", 'r') + while True: + word = f.readline() + if not word: + break + h_table.set(word, word) + f_again = open("/usr/share/dict/words", 'r') + while True: + word = f_again.readline() + if not word: + break + if word != h_table.get(word): + assert False + assert True + +def test_dictionary_attacks_me_with_fail(): + """Add a whole dictionary, change a key's value, test the changed happened.""" + not_matching = 0 + h_table = HashTable(3000) + f = open("/usr/share/dict/words", 'r') + while True: + word = f.readline() + if not word: + break + h_table.set(word, word) + h_table.set("Adirondack\n", "pickle") + f_again = open("/usr/share/dict/words", 'r') + while True: + word = f_again.readline() + if not word: + break + if word != h_table.get(word): + not_matching += 1 + assert not_matching == 1 + From 7743b627c9b371c235f48b6ce1443daaf619c453 Mon Sep 17 00:00:00 2001 From: Julien Date: Wed, 25 Jan 2017 15:03:53 -0800 Subject: [PATCH 30/72] xor hash --- src/hash_table.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/hash_table.py b/src/hash_table.py index ece271b..6e55f45 100644 --- a/src/hash_table.py +++ b/src/hash_table.py @@ -13,13 +13,22 @@ def __init__(self, size, hash_alg='additive'): def _hash(self, hash_alg): if hash_alg == 'additive': return self._additive_hash + if hash_alg == 'xor': + return self._xor_hash else: - raise ValueError("Please enter a valid hash algorithm. The options are 'additive'.") + raise ValueError("Please enter a valid hash algorithm. The options are 'additive' and 'xor'.") def _additive_hash(self, word): """Return Additive hash value.""" return sum([ord(char) for char in list(word)]) % self._size + def _xor_hash(self, word): + """Return a xor hash.""" + hash_val = 0 + for i in range(len(word)): + hash_val ^= ord(word[i]) + return hash_val + def set(self, key, value): """Set a new key-value pair in the hash table.""" if type(key) is not str: From 2c15414e3e3413cf57931cada48858410f8ea2d5 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Wed, 25 Jan 2017 15:30:14 -0800 Subject: [PATCH 31/72] added tests for xor with unix dict file --- src/test_hash_table.py | 60 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/src/test_hash_table.py b/src/test_hash_table.py index 59e892f..3329122 100644 --- a/src/test_hash_table.py +++ b/src/test_hash_table.py @@ -4,19 +4,25 @@ # import os from hash_table import HashTable + def test_simple_additive_hash(): """Test that additive hash on small word.""" h_table = HashTable(10) assert h_table._additive_hash('a') == 7 +def test_simple_xor_hash(): + """Test that additive hash on small word.""" + h_table = HashTable(10, 'xor') + assert h_table._xor_hash('at') == 21 + + def test_set_add_a_nonstring(): """Test that set() won't take in a number type.""" h_table = HashTable(10) with pytest.raises(TypeError): h_table.set(3, 'AI') - def test_set_add_word(): """Test that set() adds key-value pair.""" h_table = HashTable(10) @@ -30,6 +36,7 @@ def test_get(): h_table.set('thinking', 'tiring') assert h_table.get('thinking') == 'tiring' + def test_dictionary_attacks_me_test(): """Dictionary test.""" h_table = HashTable(3000) @@ -39,6 +46,7 @@ def test_dictionary_attacks_me_test(): if not word: break h_table.set(word, word) + f.close() f_again = open("/usr/share/dict/words", 'r') while True: word = f_again.readline() @@ -46,9 +54,11 @@ def test_dictionary_attacks_me_test(): break if word != h_table.get(word): assert False + f_again.close() assert True -def test_dictionary_attacks_me_with_fail(): + +def test_dictionary_attacks_me_with_change(): """Add a whole dictionary, change a key's value, test the changed happened.""" not_matching = 0 h_table = HashTable(3000) @@ -58,6 +68,51 @@ def test_dictionary_attacks_me_with_fail(): if not word: break h_table.set(word, word) + f.close() + h_table.set("Adirondack\n", "pickle") + f_again = open("/usr/share/dict/words", 'r') + while True: + word = f_again.readline() + if not word: + break + if word != h_table.get(word): + not_matching += 1 + f_again.close() + assert not_matching == 1 + + +def test_dictionary_test_with_xor(): + """Dictionary test.""" + h_table = HashTable(3000, 'xor') + f = open("/usr/share/dict/words", 'r') + while True: + word = f.readline() + if not word: + break + h_table.set(word, word) + f.close() + f_again = open("/usr/share/dict/words", 'r') + while True: + word = f_again.readline() + if not word: + break + if word != h_table.get(word): + assert False + f_again.close() + assert True + + +def test_dictionary_and_change_with_xor(): + """Add a whole dictionary, change a key's value, test the changed happened.""" + not_matching = 0 + h_table = HashTable(3000, 'xor') + f = open("/usr/share/dict/words", 'r') + while True: + word = f.readline() + if not word: + break + h_table.set(word, word) + f.close() h_table.set("Adirondack\n", "pickle") f_again = open("/usr/share/dict/words", 'r') while True: @@ -66,5 +121,6 @@ def test_dictionary_attacks_me_with_fail(): break if word != h_table.get(word): not_matching += 1 + f_again.close() assert not_matching == 1 From 5eddf669c6a53964976fe6d9433c8b02b1705dc4 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Wed, 25 Jan 2017 15:40:06 -0800 Subject: [PATCH 32/72] Added class docstring to module; added module info to README --- README.md | 7 +++++++ src/hash_table.py | 13 ++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0da3d09..1e3dfb6 100755 --- a/README.md +++ b/README.md @@ -46,5 +46,12 @@ Methods include: * breadth_first(self): Return a generator returns each node value from breadth-first traversal. * delete(value): Delete a node's connections (edges), effectively deleting node. Method autobalances after deletion, and tree size decrements by one. +##Hash Table +Stores key-value pairs using a given hashing algorithm. Choices for hashing algorithms are additive hash and xor hash. +Additive hash sums the Unicode code point for each letter in the word or string, then calls modulo with the number of buckets in the table. +XOR hash runs exclusive or with the letters of the word or string. +Methods include: +set(key, value): Add a key-value pair to the hash table. +get(key): Retrieve a value for the given key. diff --git a/src/hash_table.py b/src/hash_table.py index 6e55f45..29bbdd5 100644 --- a/src/hash_table.py +++ b/src/hash_table.py @@ -1,4 +1,15 @@ -"""Class for hash tables.""" +"""Class for hash tables. + +Choices for hashing algorithms are additive hash and xor hash. +Additive hash sums the Unicode code point for each letter in the word or string, +then calls modulo with the number of buckets in the table. +XOR hash runs exclusive or with the letters of the word or string. +Methods include: +set(key, value): Add a key-value pair to the hash table. +get(key): Retrieve a value for the given key. + +""" + class HashTable(object): From b0852eb39b8ecd88d9c24fdeaebdd3e7f42b93ab Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Sat, 28 Jan 2017 12:54:23 -0800 Subject: [PATCH 33/72] adding initial module file --- trie.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 trie.py diff --git a/trie.py b/trie.py new file mode 100644 index 0000000..077a956 --- /dev/null +++ b/trie.py @@ -0,0 +1 @@ +"""A class for trie trees.""" \ No newline at end of file From 720ff5f0505172f2b65b890c48aa6499431cc48c Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Sat, 28 Jan 2017 13:20:28 -0800 Subject: [PATCH 34/72] added class for trie trees and contains and insert methods --- src/trie.py | 43 +++++++++++++++++++++++++++++++++++++++++++ trie.py | 1 - 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 src/trie.py delete mode 100644 trie.py diff --git a/src/trie.py b/src/trie.py new file mode 100644 index 0000000..c711889 --- /dev/null +++ b/src/trie.py @@ -0,0 +1,43 @@ +"""This module is an implementation of a trie tree. + +Words branch out from root, with root's immediate children being +the initial letter of each word. Words can then branch from that initial, +as well as from initial substrings. + +Methods include: +contains(word): Check to see whether a word is in the tree. +insert(word): Inserts a word into the trie tree. +""" + + +class TrieTree(object): + """A class for trie trees.""" + + def __init__(self): + """Instantiate an empty trie tree.""" + self.root = Node("*") + self.size = 0 + + def contains(self, word): + """Check whether a word is in the trie tree.""" + this_node = self.root + word += "$" + for letter in word: + if letter in this_node.children: + this_node = this_node.children[letter] + if this_node == "$": + return True + return False + + def insert(self, word): + """Insert a word into the trie tree.""" + this_node = self.root + if self.contains(word): + return + word += "$" + for letter in word: + if letter in this_node.children: + this_node = this_node.chilren[letter] + else: + this_node.children[letter] = Node(letter) + self.size += 1 diff --git a/trie.py b/trie.py deleted file mode 100644 index 077a956..0000000 --- a/trie.py +++ /dev/null @@ -1 +0,0 @@ -"""A class for trie trees.""" \ No newline at end of file From bc60e6df2f95566e64588bcc0345c160079cf20f Mon Sep 17 00:00:00 2001 From: Julien Date: Sat, 28 Jan 2017 13:21:11 -0800 Subject: [PATCH 35/72] tests for insert and contains --- src/test_trie.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/test_trie.py diff --git a/src/test_trie.py b/src/test_trie.py new file mode 100644 index 0000000..af86d28 --- /dev/null +++ b/src/test_trie.py @@ -0,0 +1,38 @@ +"""Test for our implemtation of Trie Tree.""" + +import pytest + + +@pytest.fixture() +def empty_trie(): + """Build a sample trie for testing.""" + from trie import TrieTree + empty_trie = TrieTree() + return empty_trie + + +def test_trie_has_root(empty_trie): + """Test that an empty trie has a root of *.""" + assert empty_trie.root == '*' + + +def test_empty_trie_trie_size_zero(empty_trie): + """Test that an empty trie has a size of zero.""" + assert empty_trie.size == 0 + + +def test_insert_trie_increases_size(empty_trie): + """Test insertion increases the size by one.""" + empty_trie.insert('table') + assert empty_trie.size == 1 + + +def test_contains_false(empty_trie): + """Test that contains() returns false on empty trie.""" + assert not empty_trie.contains('table') + + +def test_insert_makes_word_contains_true(empty_trie): + """Test that contains returns true after inserting same word.""" + empty_trie.insert('maelstrom') + assert empty_trie.contins('maelstrom') From fc3b20becde45d266e03e6f345f8de78e5d908f2 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Sat, 28 Jan 2017 13:29:04 -0800 Subject: [PATCH 36/72] added Node class --- src/trie.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/trie.py b/src/trie.py index c711889..5eb917a 100644 --- a/src/trie.py +++ b/src/trie.py @@ -10,6 +10,16 @@ """ +class Node(object): + """A class for the tree's nodes.""" + + def __init__(self, value): + """Instantiate a node in the tree.""" + if value.isalpha(): + value = value.lower() + self.children = {} + + class TrieTree(object): """A class for trie trees.""" From 49038b719645ce64e632ef09b3cce7623428c175 Mon Sep 17 00:00:00 2001 From: Julien Date: Sat, 28 Jan 2017 14:17:44 -0800 Subject: [PATCH 37/72] more tests for insert and contains --- src/test_trie.py | 53 +++++++++++++++++++++++++++++++++++++++++++++--- src/trie.py | 41 +++++++++++++++++++++---------------- 2 files changed, 73 insertions(+), 21 deletions(-) diff --git a/src/test_trie.py b/src/test_trie.py index af86d28..324da10 100644 --- a/src/test_trie.py +++ b/src/test_trie.py @@ -12,8 +12,8 @@ def empty_trie(): def test_trie_has_root(empty_trie): - """Test that an empty trie has a root of *.""" - assert empty_trie.root == '*' + """Test that an empty trie has a root dict.""" + assert empty_trie.root == {} def test_empty_trie_trie_size_zero(empty_trie): @@ -35,4 +35,51 @@ def test_contains_false(empty_trie): def test_insert_makes_word_contains_true(empty_trie): """Test that contains returns true after inserting same word.""" empty_trie.insert('maelstrom') - assert empty_trie.contins('maelstrom') + assert empty_trie.contains('maelstrom') + + +def test_insert_makes_similar_word_contains_false(empty_trie): + """Test that contains returns true after inserting same word.""" + empty_trie.insert('maelstrom') + assert not empty_trie.contains('maelstro') + + +def test_insert_makes_similar_word_contains_false_again(empty_trie): + """Test that contains returns true after inserting same word.""" + empty_trie.insert('maelstrom') + assert not empty_trie.contains('maelstrome') + + +def test_insert_something_already_there(empty_trie): + """Test that a trie doesn't grow after inserting same word.""" + empty_trie.insert('golf') + empty_trie.insert('golf') + assert empty_trie.size == 1 + + +def test_insert_subword_adds_word(empty_trie): + """Test that adding a word that is a slice of another word adds it.""" + empty_trie.insert('golf') + empty_trie.insert('go') + assert empty_trie.size == 2 + + +def test_insert_subword_searchable(empty_trie): + """Test that adding a word that is a slice of another word adds it.""" + empty_trie.insert('golf') + empty_trie.insert('go') + assert empty_trie.contains('go') + + +def test_insert_longword(empty_trie): + """Test that adding a word that is a slice of another word adds it.""" + empty_trie.insert('golf') + empty_trie.insert('golferhole') + assert empty_trie.size == 2 + + +def test_insert_longword_searchable(empty_trie): + """Test that adding a word that is a slice of another word adds it.""" + empty_trie.insert('golf') + empty_trie.insert('golferhole') + assert empty_trie.contains('golferhole') diff --git a/src/trie.py b/src/trie.py index 5eb917a..e5bc526 100644 --- a/src/trie.py +++ b/src/trie.py @@ -7,17 +7,20 @@ Methods include: contains(word): Check to see whether a word is in the tree. insert(word): Inserts a word into the trie tree. -""" +# """ -class Node(object): - """A class for the tree's nodes.""" +# class Node(object): +# """A class for the tree's nodes.""" - def __init__(self, value): - """Instantiate a node in the tree.""" - if value.isalpha(): - value = value.lower() - self.children = {} +# def __init__(self, value): +# """Instantiate a node in the tree.""" +# if value.isalpha() or value in ['$', '*']: +# value = value.lower() +# self.children = {} +# self.value = value +# else: +# raise ValueError('That value is not acceptable.') class TrieTree(object): @@ -25,19 +28,20 @@ class TrieTree(object): def __init__(self): """Instantiate an empty trie tree.""" - self.root = Node("*") + self.root = {} self.size = 0 def contains(self, word): """Check whether a word is in the trie tree.""" this_node = self.root - word += "$" for letter in word: - if letter in this_node.children: - this_node = this_node.children[letter] - if this_node == "$": - return True - return False + if letter in this_node: + this_node = this_node[letter] + else: + return False + if '$' in this_node: + return True + return False def insert(self, word): """Insert a word into the trie tree.""" @@ -46,8 +50,9 @@ def insert(self, word): return word += "$" for letter in word: - if letter in this_node.children: - this_node = this_node.chilren[letter] + if letter in this_node: + this_node = this_node[letter] else: - this_node.children[letter] = Node(letter) + this_node[letter] = {} + this_node = this_node[letter] self.size += 1 From bd6e0ba5b59aec292784ca1e376af80c99c4dbf5 Mon Sep 17 00:00:00 2001 From: Julien Date: Sat, 28 Jan 2017 15:27:20 -0800 Subject: [PATCH 38/72] delete word --- src/trie.py | 18 ++++++++++++++++++ tox.ini | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/trie.py b/src/trie.py index e5bc526..0e42035 100644 --- a/src/trie.py +++ b/src/trie.py @@ -56,3 +56,21 @@ def insert(self, word): this_node[letter] = {} this_node = this_node[letter] self.size += 1 + + def remove(self, word): + """Remove a word from the trie.""" + if not self.contains(word): + return + word += "$" + self.bubble_down(word, self.root, 0) + + def bubble_down(self, word, node_dict, idx): + """Search for end of a word and delete it.""" + next_letter = word[idx] + if next_letter != '$': + if next_letter in node_dict: + node_dict = node_dict[next_letter] + self.bubble_down(word, node_dict, idx + 1) + if len(node_dict) > 1: + del node_dict[next_letter] + return diff --git a/tox.ini b/tox.ini index 8721979..6971cb9 100755 --- a/tox.ini +++ b/tox.ini @@ -2,6 +2,6 @@ envlist = py27, py35 [testenv] -commands = py.test src/test_bst.py +commands = py.test src/test_trie.py deps = pytest \ No newline at end of file From 87ce4b79c01565f50e3870271a2fc7fa43391def Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Sat, 28 Jan 2017 15:27:49 -0800 Subject: [PATCH 39/72] adding basic tests for remove method --- src/test_trie.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/test_trie.py b/src/test_trie.py index 324da10..c3a45be 100644 --- a/src/test_trie.py +++ b/src/test_trie.py @@ -83,3 +83,20 @@ def test_insert_longword_searchable(empty_trie): empty_trie.insert('golf') empty_trie.insert('golferhole') assert empty_trie.contains('golferhole') + + +def test_remove_absent_word(empty_trie): + """Test that removing a nonexistent word raises exception.""" + with pytest.raises(AttributeError) + empty_trie.remove("gotcha") + + +def test_remove_truly_removes(empty_trie): + """Test that remove method deletes a word from the trie tree.""" + empty_trie.insert("ephemeral") + empty_trie.remove("ephemeral") + assert empty_trie.contains("ephemeral") == False + + +# def test_remove_short_word_w_shared_root(empty_trie): +# """Test \ No newline at end of file From 86e9d10b3430acd294b7f0a92f36a6f6b113b7ac Mon Sep 17 00:00:00 2001 From: Julien Date: Sat, 28 Jan 2017 15:30:14 -0800 Subject: [PATCH 40/72] delete raises error --- src/trie.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/trie.py b/src/trie.py index 0e42035..283b11a 100644 --- a/src/trie.py +++ b/src/trie.py @@ -60,7 +60,7 @@ def insert(self, word): def remove(self, word): """Remove a word from the trie.""" if not self.contains(word): - return + raise AttributeError word += "$" self.bubble_down(word, self.root, 0) From 269c22fd411af5e20a3755a10d5e9602a5627e2b Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Sat, 28 Jan 2017 15:32:24 -0800 Subject: [PATCH 41/72] add another test for remove method --- src/test_trie.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/test_trie.py b/src/test_trie.py index c3a45be..17aa7d3 100644 --- a/src/test_trie.py +++ b/src/test_trie.py @@ -87,16 +87,20 @@ def test_insert_longword_searchable(empty_trie): def test_remove_absent_word(empty_trie): """Test that removing a nonexistent word raises exception.""" - with pytest.raises(AttributeError) - empty_trie.remove("gotcha") + with pytest.raises(AttributeError): + empty_trie.remove("gotcha") def test_remove_truly_removes(empty_trie): """Test that remove method deletes a word from the trie tree.""" empty_trie.insert("ephemeral") empty_trie.remove("ephemeral") - assert empty_trie.contains("ephemeral") == False + assert empty_trie.contains("ephemeral") is False -# def test_remove_short_word_w_shared_root(empty_trie): -# """Test \ No newline at end of file +def test_remove_short_word_w_shared_root(empty_trie): + """Test removing a word that has a longer cousin in the tree. So to speak.""" + empty_trie.insert("go") + empty_trie.insert("golf") + empty_trie.remove("go") + assert empty_trie.contains("go") is False From 794e55711a232f98ae5dffe1d48b7583170ebc04 Mon Sep 17 00:00:00 2001 From: Julien Date: Sat, 28 Jan 2017 15:38:29 -0800 Subject: [PATCH 42/72] delete works on single word trees --- src/trie.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/trie.py b/src/trie.py index 283b11a..fbf7790 100644 --- a/src/trie.py +++ b/src/trie.py @@ -61,6 +61,9 @@ def remove(self, word): """Remove a word from the trie.""" if not self.contains(word): raise AttributeError + if self.size == 1: + self.root = {} + self.size = 0 word += "$" self.bubble_down(word, self.root, 0) @@ -72,5 +75,6 @@ def bubble_down(self, word, node_dict, idx): node_dict = node_dict[next_letter] self.bubble_down(word, node_dict, idx + 1) if len(node_dict) > 1: + self.size -= 1 del node_dict[next_letter] return From 64e09fd1f5f916173345de0665d0b4663fe5cd5d Mon Sep 17 00:00:00 2001 From: Julien Date: Sat, 28 Jan 2017 15:40:48 -0800 Subject: [PATCH 43/72] added size() --- src/trie.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/trie.py b/src/trie.py index fbf7790..4aa6f79 100644 --- a/src/trie.py +++ b/src/trie.py @@ -29,7 +29,7 @@ class TrieTree(object): def __init__(self): """Instantiate an empty trie tree.""" self.root = {} - self.size = 0 + self._size = 0 def contains(self, word): """Check whether a word is in the trie tree.""" @@ -55,17 +55,17 @@ def insert(self, word): else: this_node[letter] = {} this_node = this_node[letter] - self.size += 1 + self._size += 1 def remove(self, word): """Remove a word from the trie.""" if not self.contains(word): raise AttributeError - if self.size == 1: + if self._size == 1: self.root = {} - self.size = 0 + self._size = 0 word += "$" - self.bubble_down(word, self.root, 0) + self._bubble_down(word, self.root, 0) def bubble_down(self, word, node_dict, idx): """Search for end of a word and delete it.""" @@ -73,8 +73,12 @@ def bubble_down(self, word, node_dict, idx): if next_letter != '$': if next_letter in node_dict: node_dict = node_dict[next_letter] - self.bubble_down(word, node_dict, idx + 1) + self._bubble_down(word, node_dict, idx + 1) if len(node_dict) > 1: - self.size -= 1 + self._size -= 1 del node_dict[next_letter] return + + def size(self): + """Retrun the size of the trie.""" + return self._size From 4664d20b1f35cfd51a1e0e8d1f1dbb9526395848 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Sat, 28 Jan 2017 15:43:12 -0800 Subject: [PATCH 44/72] add more tests for remove method --- src/test_trie.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/test_trie.py b/src/test_trie.py index 17aa7d3..bb58e30 100644 --- a/src/test_trie.py +++ b/src/test_trie.py @@ -104,3 +104,28 @@ def test_remove_short_word_w_shared_root(empty_trie): empty_trie.insert("golf") empty_trie.remove("go") assert empty_trie.contains("go") is False + + +def test_remove_short_word_w_shared_root_keeps_longer_word(empty_trie): + """Test removing a word that has a longer cousin in the tree keeps long word.""" + empty_trie.insert("go") + empty_trie.insert("golf") + empty_trie.remove("go") + assert empty_trie.contains("golf") is True + + +def test_remove_word_w_shorter_root_word(empty_trie): + """Test removing a word that has a shorter cousin in the tree.""" + empty_trie.insert("go") + empty_trie.insert("golf") + empty_trie.remove("golf") + assert empty_trie.contains("golf") is False + +def test_remove_word_w_shorter_root_word_keeps_shorter_one(empty_trie): + """Test that removing a word with a word root also in tree keeps the short word.""" + empty_trie.insert("go") + empty_trie.insert("golf") + empty_trie.remove("golf") + assert empty_trie.contains("go") is True + + From baddf4985f5d29cd723166cb8dd4ee0aee7e7572 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Sat, 28 Jan 2017 15:47:58 -0800 Subject: [PATCH 45/72] debugging remove's helper method, tests --- src/test_trie.py | 10 +++++----- src/trie.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test_trie.py b/src/test_trie.py index bb58e30..70bd943 100644 --- a/src/test_trie.py +++ b/src/test_trie.py @@ -18,13 +18,13 @@ def test_trie_has_root(empty_trie): def test_empty_trie_trie_size_zero(empty_trie): """Test that an empty trie has a size of zero.""" - assert empty_trie.size == 0 + assert empty_trie.size() == 0 def test_insert_trie_increases_size(empty_trie): """Test insertion increases the size by one.""" empty_trie.insert('table') - assert empty_trie.size == 1 + assert empty_trie.size() == 1 def test_contains_false(empty_trie): @@ -54,14 +54,14 @@ def test_insert_something_already_there(empty_trie): """Test that a trie doesn't grow after inserting same word.""" empty_trie.insert('golf') empty_trie.insert('golf') - assert empty_trie.size == 1 + assert empty_trie.size() == 1 def test_insert_subword_adds_word(empty_trie): """Test that adding a word that is a slice of another word adds it.""" empty_trie.insert('golf') empty_trie.insert('go') - assert empty_trie.size == 2 + assert empty_trie.size() == 2 def test_insert_subword_searchable(empty_trie): @@ -75,7 +75,7 @@ def test_insert_longword(empty_trie): """Test that adding a word that is a slice of another word adds it.""" empty_trie.insert('golf') empty_trie.insert('golferhole') - assert empty_trie.size == 2 + assert empty_trie.size() == 2 def test_insert_longword_searchable(empty_trie): diff --git a/src/trie.py b/src/trie.py index 4aa6f79..e4d01bc 100644 --- a/src/trie.py +++ b/src/trie.py @@ -67,7 +67,7 @@ def remove(self, word): word += "$" self._bubble_down(word, self.root, 0) - def bubble_down(self, word, node_dict, idx): + def _bubble_down(self, word, node_dict, idx): """Search for end of a word and delete it.""" next_letter = word[idx] if next_letter != '$': From b7cfccf20bb5dd4fe66bbf16c1ba6d9df9d20a08 Mon Sep 17 00:00:00 2001 From: Julien Date: Sat, 28 Jan 2017 16:01:33 -0800 Subject: [PATCH 46/72] deletes word parts and extensions --- src/trie.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/trie.py b/src/trie.py index e4d01bc..e927df9 100644 --- a/src/trie.py +++ b/src/trie.py @@ -69,6 +69,7 @@ def remove(self, word): def _bubble_down(self, word, node_dict, idx): """Search for end of a word and delete it.""" + # import pdb; pdb.set_trace() next_letter = word[idx] if next_letter != '$': if next_letter in node_dict: @@ -76,6 +77,8 @@ def _bubble_down(self, word, node_dict, idx): self._bubble_down(word, node_dict, idx + 1) if len(node_dict) > 1: self._size -= 1 + if next_letter != '$': + next_letter = word[idx + 1] del node_dict[next_letter] return From 474714a5a5e6617cd283c7ed0b02dc73cf66cbd5 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Sat, 28 Jan 2017 16:08:47 -0800 Subject: [PATCH 47/72] add tests for size method --- src/test_trie.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/test_trie.py b/src/test_trie.py index 70bd943..71cbb9f 100644 --- a/src/test_trie.py +++ b/src/test_trie.py @@ -129,3 +129,21 @@ def test_remove_word_w_shorter_root_word_keeps_shorter_one(empty_trie): assert empty_trie.contains("go") is True +def test_size_on_fresh_empty_tree(empty_trie): + """Test calling size on empty tree.""" + assert empty_trie.size() == 0 + + +def test_size_on_tree_with_one_word(empty_trie): + """Test calling size on tree with one word.""" + empty_trie.insert("minimal") + assert empty_trie.size() == 1 + + +def test_size_after_remove(empty_trie): + """Test calling size on tree updates after remove.""" + empty_trie.insert("ephemeral") + empty_trie.remove("ephemeral") + assert empty_trie.size() == 0 + + From 1f64fbb2ac62782356a71f747386c6344c94c3b5 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Sat, 28 Jan 2017 16:24:33 -0800 Subject: [PATCH 48/72] adding docstrings, README info --- README.md | 11 +++++++++++ src/trie.py | 19 +++---------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 1e3dfb6..818fbd7 100755 --- a/README.md +++ b/README.md @@ -55,3 +55,14 @@ Methods include: set(key, value): Add a key-value pair to the hash table. get(key): Retrieve a value for the given key. +##Trie Trees +Module is an implementation of a trie tree, using nested dictionaries instead of nodes. + +Words branch out from root, with root's immediate children being +the initial letter of each word. Words can then branch from that initial, +as well as from initial substrings. + +Methods include: +contains(word): Check to see whether a word is in the tree. O(k), where k is the length of the given word. +insert(word): Inserts a word into the trie tree. O(k), where k is the length of the given word. + diff --git a/src/trie.py b/src/trie.py index e927df9..4c0e11e 100644 --- a/src/trie.py +++ b/src/trie.py @@ -5,22 +5,9 @@ as well as from initial substrings. Methods include: -contains(word): Check to see whether a word is in the tree. -insert(word): Inserts a word into the trie tree. -# """ - - -# class Node(object): -# """A class for the tree's nodes.""" - -# def __init__(self, value): -# """Instantiate a node in the tree.""" -# if value.isalpha() or value in ['$', '*']: -# value = value.lower() -# self.children = {} -# self.value = value -# else: -# raise ValueError('That value is not acceptable.') +contains(word): Check to see whether a word is in the tree. O(k), where k is the length of the given word. +insert(word): Inserts a word into the trie tree. O(k), where k is the length of the given word. +""" class TrieTree(object): From 416b704dc59d0d7fade8992715a4838e121892a4 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Sat, 28 Jan 2017 16:43:08 -0800 Subject: [PATCH 49/72] adding initial traversal method --- src/trie.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/trie.py b/src/trie.py index 4c0e11e..9fbdf33 100644 --- a/src/trie.py +++ b/src/trie.py @@ -72,3 +72,8 @@ def _bubble_down(self, word, node_dict, idx): def size(self): """Retrun the size of the trie.""" return self._size + + + def traversal(self, word): + """.""" + pass From 1e83927946b918ad9a9c4953165bad3d19cafd82 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Sat, 28 Jan 2017 16:47:53 -0800 Subject: [PATCH 50/72] added traversal and helper methods --- src/trie.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/trie.py b/src/trie.py index 9fbdf33..e8012ba 100644 --- a/src/trie.py +++ b/src/trie.py @@ -76,4 +76,18 @@ def size(self): def traversal(self, word): """.""" - pass + node = self.root + try: + for letter in word: + node = node[letter] + rec_trav(node) + except: + IndexError + + def rec_trav(node): + """.""" + for value in node: + if not "$": + yield + node = node[value] + rec_trav(node) From cec048e644a0ee47ccde9c1e986497533de8a6f4 Mon Sep 17 00:00:00 2001 From: Julien Date: Sat, 28 Jan 2017 16:49:12 -0800 Subject: [PATCH 51/72] test for traversal --- src/test_trie.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/test_trie.py b/src/test_trie.py index 71cbb9f..6f2917e 100644 --- a/src/test_trie.py +++ b/src/test_trie.py @@ -11,6 +11,18 @@ def empty_trie(): return empty_trie +@pytest.fixture() +def boop_trie(): + """Build another sample trie for testing.""" + from trie import TrieTree + boop_trie = TrieTree() + boop_trie.insert('bacon') + boop_trie.insert('boop') + boop_trie.insert('bolt') + boop_trie.insert('bolted') + return boop_trie + + def test_trie_has_root(empty_trie): """Test that an empty trie has a root dict.""" assert empty_trie.root == {} @@ -121,6 +133,7 @@ def test_remove_word_w_shorter_root_word(empty_trie): empty_trie.remove("golf") assert empty_trie.contains("golf") is False + def test_remove_word_w_shorter_root_word_keeps_shorter_one(empty_trie): """Test that removing a word with a word root also in tree keeps the short word.""" empty_trie.insert("go") @@ -147,3 +160,14 @@ def test_size_after_remove(empty_trie): assert empty_trie.size() == 0 +def test_boop_tree_traversal(boop_trie): + """Test the traversal method on a boop tree.""" + trav_gen = boop_trie.traversal('bo') + trave_list = [] + trave_list.append(next(trav_gen) + trave_list.append(next(trav_gen) + trave_list.append(next(trav_gen) + trave_list.append(next(trav_gen) + trave_list.append(next(trav_gen) + trave_list.append(next(trav_gen) + assert trave_list == ['o', 'b', 'l', 't', 'e', 'd'] \ No newline at end of file From 7f92d13b136b37108365fe523062677f9c247f36 Mon Sep 17 00:00:00 2001 From: Julien Date: Sat, 28 Jan 2017 17:34:18 -0800 Subject: [PATCH 52/72] test for traversal is a mess --- src/test_trie.py | 14 +++++++------- src/trie.py | 15 +++++++-------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/test_trie.py b/src/test_trie.py index 6f2917e..c5372e0 100644 --- a/src/test_trie.py +++ b/src/test_trie.py @@ -164,10 +164,10 @@ def test_boop_tree_traversal(boop_trie): """Test the traversal method on a boop tree.""" trav_gen = boop_trie.traversal('bo') trave_list = [] - trave_list.append(next(trav_gen) - trave_list.append(next(trav_gen) - trave_list.append(next(trav_gen) - trave_list.append(next(trav_gen) - trave_list.append(next(trav_gen) - trave_list.append(next(trav_gen) - assert trave_list == ['o', 'b', 'l', 't', 'e', 'd'] \ No newline at end of file + trave_list.append(next(trav_gen)) + trave_list.append(next(trav_gen)) + trave_list.append(next(trav_gen)) + trave_list.append(next(trav_gen)) + trave_list.append(next(trav_gen)) + trave_list.append(next(trav_gen)) + assert trave_list is ['o', 'b', 'l', 't', 'e', 'd'] \ No newline at end of file diff --git a/src/trie.py b/src/trie.py index e8012ba..447bd4b 100644 --- a/src/trie.py +++ b/src/trie.py @@ -56,7 +56,6 @@ def remove(self, word): def _bubble_down(self, word, node_dict, idx): """Search for end of a word and delete it.""" - # import pdb; pdb.set_trace() next_letter = word[idx] if next_letter != '$': if next_letter in node_dict: @@ -73,21 +72,21 @@ def size(self): """Retrun the size of the trie.""" return self._size - def traversal(self, word): """.""" node = self.root try: for letter in word: node = node[letter] - rec_trav(node) + return self.rec_trav(node) except: IndexError - def rec_trav(node): + def rec_trav(self, node): """.""" - for value in node: - if not "$": - yield + keys = sorted(node.keys()) + for value in keys: + if value is not "$": + yield value node = node[value] - rec_trav(node) + yield from self.rec_trav(node) From 3ecd96d44a1fe30a09f250d60fcec4ff5e8294f9 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Mon, 30 Jan 2017 13:54:45 -0800 Subject: [PATCH 53/72] draft function written --- src/insertion_sort.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/insertion_sort.py diff --git a/src/insertion_sort.py b/src/insertion_sort.py new file mode 100644 index 0000000..a2fecca --- /dev/null +++ b/src/insertion_sort.py @@ -0,0 +1,16 @@ +"""Insertion sort.""" + +def insertion_sort(some_list): + """some doc.""" + if not hasattr(some_list, "__iter__"): + raise(TypeError) + idx = 0 + while idx < len(some_list) - 1: + if some_list[idx] > some_list[idx + 1]: + some_list[idx], some_list[idx + 1] = some_list[idx + 1], some_list[idx] + for i in some_list[:idx:-1]: + import pdb; pdb.set_trace() + if some_list[idx] < i: + some_list[idx], i = i, some_list[idx] + idx += 1 + return some_list From e431eaf30eff8d0b219632a5e333cc7d87ac783d Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Mon, 30 Jan 2017 13:55:37 -0800 Subject: [PATCH 54/72] adding to repo --- src/insertion_sort.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/insertion_sort.py b/src/insertion_sort.py index a2fecca..79f8c28 100644 --- a/src/insertion_sort.py +++ b/src/insertion_sort.py @@ -1,7 +1,7 @@ """Insertion sort.""" def insertion_sort(some_list): - """some doc.""" + """It sorts.""" if not hasattr(some_list, "__iter__"): raise(TypeError) idx = 0 From dd383b76f727f9a6eeb33dcd920df614d7cb0868 Mon Sep 17 00:00:00 2001 From: Julien Date: Tue, 31 Jan 2017 10:29:42 -0800 Subject: [PATCH 55/72] fixed insertion sort bug --- src/insertion_sort.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/insertion_sort.py b/src/insertion_sort.py index 79f8c28..aa9e5df 100644 --- a/src/insertion_sort.py +++ b/src/insertion_sort.py @@ -8,9 +8,12 @@ def insertion_sort(some_list): while idx < len(some_list) - 1: if some_list[idx] > some_list[idx + 1]: some_list[idx], some_list[idx + 1] = some_list[idx + 1], some_list[idx] - for i in some_list[:idx:-1]: - import pdb; pdb.set_trace() - if some_list[idx] < i: - some_list[idx], i = i, some_list[idx] + bw_list = some_list[:idx][::-1] + bw_idx = idx + for i in range(len(bw_list)): + # import pdb; pdb.set_trace() + if some_list[bw_idx] < bw_list[i]: + some_list[bw_idx], bw_list[i] = bw_list[i], some_list[bw_idx] + bw_idx -= 1 idx += 1 return some_list From 9ff3e3b93110e23d31499ff26e4670da2621903d Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Tue, 31 Jan 2017 13:53:47 -0800 Subject: [PATCH 56/72] adding draft function, helper function --- src/merge_sort.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/merge_sort.py diff --git a/src/merge_sort.py b/src/merge_sort.py new file mode 100644 index 0000000..dd199d1 --- /dev/null +++ b/src/merge_sort.py @@ -0,0 +1,28 @@ +"""Merge sort implementation.""" + +def merge_sort(a_list): + """Divide, conquer to sort list.""" + if len(a_list) <= 1: + return a_list + midpoint = (len(a_list) // 2) + first_half = a_list[:midpoint] + second_half = a_list[midpoint:] + merge_sort(first_half) + merge_sort(second_half) + return merge(left, right) + +def merge(list1, list2): + """The helper function to do the comparisons.""" + result = [ ] + while list1 and list2: + if list1[0] <= list2[0]: + result.append(list1.pop(0)) + else: + result.append(list2.pop(0)) + if list1: + result += list1 + if list2: + result += list2 + return result + + From 6bbd43d4b60a3f2947420f5ec999ceb2ca7d1052 Mon Sep 17 00:00:00 2001 From: Julien Date: Tue, 31 Jan 2017 13:55:04 -0800 Subject: [PATCH 57/72] test merge sort --- src/test_merge_sort.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/test_merge_sort.py diff --git a/src/test_merge_sort.py b/src/test_merge_sort.py new file mode 100644 index 0000000..1806608 --- /dev/null +++ b/src/test_merge_sort.py @@ -0,0 +1,20 @@ +"""Tests for merge sort.""" +from merge_sort import merge_sort + +import pytest + + +ASSERTIONS = [ + ([34, 45, 76, 1, 3, 5, 6, 8, 3, 98, 654, 23, 0, 3, 100], + [0, 1, 3, 3, 3, 5, 6, 8, 23, 34, 45, 76, 98, 100, 654]), + ([54, 65, 34, 7, 4, 2, 5, 7876, 34, 90, 17, 32, 7, 5, 23, 83], + [2, 4, 5, 5, 7, 7, 17, 23, 32, 34, 34, 54, 65, 83, 90, 7876]), + ([0, 4, 6, 13, 0, 6, 4, 0, 6, 7, 3], + [0, 0, 0, 3, 4, 4, 6, 6, 6, 7, 13]), +] + + +@pytest.mark.parametrize("unsorted_list, sorted_list", ASSERTIONS) +def test_merge_sort(unsorted_list, sorted_list): + """Test for merge sort.""" + assert merge_sort(unsorted_list) == sorted_list From 55420a8e29d423c160dd74d4b7bed4bfe66ce3cf Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Tue, 31 Jan 2017 14:10:39 -0800 Subject: [PATCH 58/72] reverting helper functions final ifs --- src/merge_sort.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/merge_sort.py b/src/merge_sort.py index dd199d1..756bdb9 100644 --- a/src/merge_sort.py +++ b/src/merge_sort.py @@ -1,6 +1,7 @@ """Merge sort implementation.""" -def merge_sort(a_list): + +def merge_sort(a_list): """Divide, conquer to sort list.""" if len(a_list) <= 1: return a_list @@ -9,20 +10,19 @@ def merge_sort(a_list): second_half = a_list[midpoint:] merge_sort(first_half) merge_sort(second_half) - return merge(left, right) + return merge(first_half, second_half) + def merge(list1, list2): """The helper function to do the comparisons.""" - result = [ ] + result = [] while list1 and list2: if list1[0] <= list2[0]: result.append(list1.pop(0)) else: result.append(list2.pop(0)) if list1: - result += list1 + result.append(list1.pop(0)) if list2: - result += list2 + result.append(list2.pop(0)) return result - - From 4897c730cfaea581baa6e65f8a397424c53397b8 Mon Sep 17 00:00:00 2001 From: Julien Date: Tue, 31 Jan 2017 14:11:39 -0800 Subject: [PATCH 59/72] not sorting, some tests --- src/merge_sort.py | 12 +++++++----- src/test_merge_sort.py | 18 ++++++++++++------ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/merge_sort.py b/src/merge_sort.py index dd199d1..70a33e0 100644 --- a/src/merge_sort.py +++ b/src/merge_sort.py @@ -1,7 +1,9 @@ """Merge sort implementation.""" -def merge_sort(a_list): + +def merge_sort(a_list): """Divide, conquer to sort list.""" + import pdb; pdb.set_trace() if len(a_list) <= 1: return a_list midpoint = (len(a_list) // 2) @@ -9,11 +11,13 @@ def merge_sort(a_list): second_half = a_list[midpoint:] merge_sort(first_half) merge_sort(second_half) - return merge(left, right) + return merge(first_half, second_half) + def merge(list1, list2): """The helper function to do the comparisons.""" - result = [ ] + import pdb; pdb.set_trace() + result = [] while list1 and list2: if list1[0] <= list2[0]: result.append(list1.pop(0)) @@ -24,5 +28,3 @@ def merge(list1, list2): if list2: result += list2 return result - - diff --git a/src/test_merge_sort.py b/src/test_merge_sort.py index 1806608..3c06c6a 100644 --- a/src/test_merge_sort.py +++ b/src/test_merge_sort.py @@ -5,12 +5,18 @@ ASSERTIONS = [ - ([34, 45, 76, 1, 3, 5, 6, 8, 3, 98, 654, 23, 0, 3, 100], - [0, 1, 3, 3, 3, 5, 6, 8, 23, 34, 45, 76, 98, 100, 654]), - ([54, 65, 34, 7, 4, 2, 5, 7876, 34, 90, 17, 32, 7, 5, 23, 83], - [2, 4, 5, 5, 7, 7, 17, 23, 32, 34, 34, 54, 65, 83, 90, 7876]), - ([0, 4, 6, 13, 0, 6, 4, 0, 6, 7, 3], - [0, 0, 0, 3, 4, 4, 6, 6, 6, 7, 13]), + ([34, 45, 20, 1, 3, 5, 65, 100], + [1, 3, 5, 20, 34, 45, 65, 100]), + # ([34, 45, 76, 1, 3, 5, 6, 8, 3, 98, 654, 23, 0, 3, 100], + # [0, 1, 3, 3, 3, 5, 6, 8, 23, 34, 45, 76, 98, 100, 654]), + # ([54, 65, 34, 7, 4, 2, 5, 7876, 34, 90, 17, 32, 7, 5, 23, 83], + # [2, 4, 5, 5, 7, 7, 17, 23, 32, 34, 34, 54, 65, 83, 90, 7876]), + # ([0, 4, 6, 13, 0, 6, 4, 0, 6, 7, 3], + # [0, 0, 0, 3, 4, 4, 6, 6, 6, 7, 13]), + # ([0, 4, 6, 13], + # [0, 4, 6, 13]), + # ([4, 13, 0, 6], + # [0, 4, 6, 13]), ] From 3b39110795484572dcaf8b1f591bbedbc0801669 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Tue, 31 Jan 2017 14:12:14 -0800 Subject: [PATCH 60/72] undoing the revert. not working anyway tho --- src/merge_sort.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/merge_sort.py b/src/merge_sort.py index 756bdb9..9091e03 100644 --- a/src/merge_sort.py +++ b/src/merge_sort.py @@ -22,7 +22,7 @@ def merge(list1, list2): else: result.append(list2.pop(0)) if list1: - result.append(list1.pop(0)) + result += list1 if list2: - result.append(list2.pop(0)) + result += list2 return result From 5592fb86687f0d5edacc44e4635be112f4918962 Mon Sep 17 00:00:00 2001 From: Julien Date: Tue, 31 Jan 2017 14:16:08 -0800 Subject: [PATCH 61/72] its worrrking --- src/merge_sort.py | 8 ++++---- src/test_merge_sort.py | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/merge_sort.py b/src/merge_sort.py index 70a33e0..6a68050 100644 --- a/src/merge_sort.py +++ b/src/merge_sort.py @@ -3,20 +3,20 @@ def merge_sort(a_list): """Divide, conquer to sort list.""" - import pdb; pdb.set_trace() + # import pdb; pdb.set_trace() if len(a_list) <= 1: return a_list midpoint = (len(a_list) // 2) first_half = a_list[:midpoint] second_half = a_list[midpoint:] - merge_sort(first_half) - merge_sort(second_half) + first_half = merge_sort(first_half) + second_half = merge_sort(second_half) return merge(first_half, second_half) def merge(list1, list2): """The helper function to do the comparisons.""" - import pdb; pdb.set_trace() + # import pdb; pdb.set_trace() result = [] while list1 and list2: if list1[0] <= list2[0]: diff --git a/src/test_merge_sort.py b/src/test_merge_sort.py index 3c06c6a..6150d90 100644 --- a/src/test_merge_sort.py +++ b/src/test_merge_sort.py @@ -7,16 +7,16 @@ ASSERTIONS = [ ([34, 45, 20, 1, 3, 5, 65, 100], [1, 3, 5, 20, 34, 45, 65, 100]), - # ([34, 45, 76, 1, 3, 5, 6, 8, 3, 98, 654, 23, 0, 3, 100], - # [0, 1, 3, 3, 3, 5, 6, 8, 23, 34, 45, 76, 98, 100, 654]), - # ([54, 65, 34, 7, 4, 2, 5, 7876, 34, 90, 17, 32, 7, 5, 23, 83], - # [2, 4, 5, 5, 7, 7, 17, 23, 32, 34, 34, 54, 65, 83, 90, 7876]), - # ([0, 4, 6, 13, 0, 6, 4, 0, 6, 7, 3], - # [0, 0, 0, 3, 4, 4, 6, 6, 6, 7, 13]), - # ([0, 4, 6, 13], - # [0, 4, 6, 13]), - # ([4, 13, 0, 6], - # [0, 4, 6, 13]), + ([34, 45, 76, 1, 3, 5, 6, 8, 3, 98, 654, 23, 0, 3, 100], + [0, 1, 3, 3, 3, 5, 6, 8, 23, 34, 45, 76, 98, 100, 654]), + ([54, 65, 34, 7, 4, 2, 5, 7876, 34, 90, 17, 32, 7, 5, 23, 83], + [2, 4, 5, 5, 7, 7, 17, 23, 32, 34, 34, 54, 65, 83, 90, 7876]), + ([0, 4, 6, 13, 0, 6, 4, 0, 6, 7, 3], + [0, 0, 0, 3, 4, 4, 6, 6, 6, 7, 13]), + ([0, 4, 6, 13], + [0, 4, 6, 13]), + ([4, 13, 0, 6], + [0, 4, 6, 13]), ] From c205e521134531f58a071974d80b2eac15cdac32 Mon Sep 17 00:00:00 2001 From: Julien Date: Tue, 31 Jan 2017 14:33:50 -0800 Subject: [PATCH 62/72] merge sort timit --- src/insertion_sort.py | 1 + src/merge_sort.py | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/insertion_sort.py b/src/insertion_sort.py index aa9e5df..4b01864 100644 --- a/src/insertion_sort.py +++ b/src/insertion_sort.py @@ -17,3 +17,4 @@ def insertion_sort(some_list): bw_idx -= 1 idx += 1 return some_list + diff --git a/src/merge_sort.py b/src/merge_sort.py index 6a68050..7df1eae 100644 --- a/src/merge_sort.py +++ b/src/merge_sort.py @@ -28,3 +28,28 @@ def merge(list1, list2): if list2: result += list2 return result + + +if __name__ == "__main__": + + import timeit + import random + + def build_random_list(): + """Build a random list to sort.""" + rand_list = [random.randint(0, 1000) for i in range(200)] + return rand_list + + lst = build_random_list() + + print(timeit.repeat(stmt='merge_sort(lst)', + setup='from __main__ import merge_sort, lst, random', repeat=3, + number=1000 + ) + ) + + # print(timeit.repeat(stmt='g.breadth_first_traversal(random.choice(list(g.node_dict.keys())))', + # setup='from __main__ import SimpleGraph, g, random', repeat=3, + # number=1000 + # ) + # ) From 7f6208a0c0539dc6ba3f678c2fdebaf2b8abe529 Mon Sep 17 00:00:00 2001 From: Julien Date: Tue, 31 Jan 2017 14:34:33 -0800 Subject: [PATCH 63/72] timit insertion sort --- src/insertion_sort.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/insertion_sort.py b/src/insertion_sort.py index aa9e5df..300d455 100644 --- a/src/insertion_sort.py +++ b/src/insertion_sort.py @@ -17,3 +17,21 @@ def insertion_sort(some_list): bw_idx -= 1 idx += 1 return some_list + +if __name__ == "__main__": + + import timeit + import random + + def build_random_list(): + """Build a random list to sort.""" + rand_list = [random.randint(0, 1000) for i in range(200)] + return rand_list + + lst = build_random_list() + + print(timeit.repeat(stmt='insertion_sort(lst)', + setup='from __main__ import insertion_sort, lst, random', repeat=3, + number=1000 + ) + ) From 2f3ebfa56c7bf233effa743d111e004a5ef346b4 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Wed, 1 Feb 2017 12:32:36 -0800 Subject: [PATCH 64/72] adding first draft of quicksort --- src/quicksort.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/quicksort.py diff --git a/src/quicksort.py b/src/quicksort.py new file mode 100644 index 0000000..0b42034 --- /dev/null +++ b/src/quicksort.py @@ -0,0 +1,11 @@ +"""Implementation of quicksort.""" + +def quicksort(array): + """Divide, sort, conquer, the quicksort way.""" + if len(array) < 2: + return array + else: + pivot = array[0] + less = [i for i in array[1:] if i <= pivot] + greater = [i for i in array[1:] if i > pivot] + return quicksort(less) + [pivot] + quicksort(greater) From 90844904cfccabdd088f02ca52a38b317f6849a6 Mon Sep 17 00:00:00 2001 From: Julien Date: Wed, 1 Feb 2017 12:47:30 -0800 Subject: [PATCH 65/72] quicksort timit --- src/insertion_sort.py | 1 + src/quicksort.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/insertion_sort.py b/src/insertion_sort.py index 300d455..3e9e49b 100644 --- a/src/insertion_sort.py +++ b/src/insertion_sort.py @@ -1,5 +1,6 @@ """Insertion sort.""" + def insertion_sort(some_list): """It sorts.""" if not hasattr(some_list, "__iter__"): diff --git a/src/quicksort.py b/src/quicksort.py index 0b42034..490405e 100644 --- a/src/quicksort.py +++ b/src/quicksort.py @@ -1,5 +1,6 @@ """Implementation of quicksort.""" + def quicksort(array): """Divide, sort, conquer, the quicksort way.""" if len(array) < 2: @@ -9,3 +10,22 @@ def quicksort(array): less = [i for i in array[1:] if i <= pivot] greater = [i for i in array[1:] if i > pivot] return quicksort(less) + [pivot] + quicksort(greater) + + +if __name__ == "__main__": + + import timeit + import random + + def build_random_list(): + """Build a random list to sort.""" + rand_list = [random.randint(0, 10000) for i in range(200)] + return rand_list + + lst = build_random_list() + + print(timeit.repeat(stmt='quicksort(lst)', + setup='from __main__ import quicksort, lst, random', repeat=3, + number=10000 + ) + ) From f51591f66b7626abd8df695cabb63211fcdab30e Mon Sep 17 00:00:00 2001 From: Julien Date: Wed, 1 Feb 2017 12:53:11 -0800 Subject: [PATCH 66/72] test for quicksort --- src/test_quicksort.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/test_quicksort.py diff --git a/src/test_quicksort.py b/src/test_quicksort.py new file mode 100644 index 0000000..ceb421f --- /dev/null +++ b/src/test_quicksort.py @@ -0,0 +1,25 @@ +from quicksort import quicksort + +import pytest + + +ASSERTIONS = [ + ([34, 45, 20, 1, 3, 5, 65, 100], + [1, 3, 5, 20, 34, 45, 65, 100]), + ([34, 45, 76, 1, 3, 5, 6, 8, 3, 98, 654, 23, 0, 3, 100], + [0, 1, 3, 3, 3, 5, 6, 8, 23, 34, 45, 76, 98, 100, 654]), + ([54, 65, 34, 7, 4, 2, 5, 7876, 34, 90, 17, 32, 7, 5, 23, 83], + [2, 4, 5, 5, 7, 7, 17, 23, 32, 34, 34, 54, 65, 83, 90, 7876]), + ([0, 4, 6, 13, 0, 6, 4, 0, 6, 7, 3], + [0, 0, 0, 3, 4, 4, 6, 6, 6, 7, 13]), + ([0, 4, 6, 13], + [0, 4, 6, 13]), + ([4, 13, 0, 6], + [0, 4, 6, 13]), +] + + +@pytest.mark.parametrize("unsorted_list, sorted_list", ASSERTIONS) +def test_quicksort(unsorted_list, sorted_list): + """Test for quicksort.""" + assert quicksort(unsorted_list) == sorted_list From 37f4dc7eda25fd90452c413c85ce26aec0e4d677 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Wed, 1 Feb 2017 13:06:08 -0800 Subject: [PATCH 67/72] edited README, module docstrings --- README.md | 7 +++++++ src/merge_sort.py | 22 +++++++++++++--------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 818fbd7..cba51d3 100755 --- a/README.md +++ b/README.md @@ -66,3 +66,10 @@ Methods include: contains(word): Check to see whether a word is in the tree. O(k), where k is the length of the given word. insert(word): Inserts a word into the trie tree. O(k), where k is the length of the given word. +##Merge Sort +This implementation of the merge sort algorithm recursively divides the input list into sublists small enough to be sorted on their own, then merges them. +When run as a script, a timeit function runs merge_sort() on a list of 200 random integers three times, and returns the run time for each. + +Methods include: +merge_sort(a_list): Recursively divides list at midpoint, more or less. +merge(list1, list2): A helper function to do the comparisons between values. \ No newline at end of file diff --git a/src/merge_sort.py b/src/merge_sort.py index efd553e..25d8cc4 100644 --- a/src/merge_sort.py +++ b/src/merge_sort.py @@ -1,9 +1,19 @@ -"""Merge sort implementation.""" +"""Implementation of merge sort. + +This version recusrively divides the input list into sublists small enough +to be sorted on their own, then merges them. When run as a script, +a timeit function runs merge_sort() on a list of 200 random integers +three times, and returns the run time for each. + + +Methods include: +merge_sort(a_list): Recursively divides list at midpoint, more or less. +merge(list1, list2): A helper function to do the comparisons between values. +""" def merge_sort(a_list): """Divide, conquer to sort list.""" - # import pdb; pdb.set_trace() if len(a_list) <= 1: return a_list midpoint = (len(a_list) // 2) @@ -45,10 +55,4 @@ def build_random_list(): setup='from __main__ import merge_sort, lst, random', repeat=3, number=1000 ) - ) - - # print(timeit.repeat(stmt='g.breadth_first_traversal(random.choice(list(g.node_dict.keys())))', - # setup='from __main__ import SimpleGraph, g, random', repeat=3, - # number=1000 - # ) - # ) + ) \ No newline at end of file From 54fdde4a97c520e355bf7b42add3ad24e3a829b2 Mon Sep 17 00:00:00 2001 From: Julien Date: Thu, 2 Feb 2017 11:54:14 -0800 Subject: [PATCH 68/72] starting radix --- src/radix_sort.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/radix_sort.py diff --git a/src/radix_sort.py b/src/radix_sort.py new file mode 100644 index 0000000..374cf9f --- /dev/null +++ b/src/radix_sort.py @@ -0,0 +1,16 @@ +"""Implementation of a Radix Sort.""" +import math + + +def radix_sort(iter): + """Sort a list using the radix sort method.""" + number_holder = {} + for number in iter: + number_holder[number % 10] = number + return number_holder + + + + +def rounddown(x, mag): + return int(math.floor(x / mag)) * mag From b701e852f33a1b6b106abc41a7234c2fb1e0b8c3 Mon Sep 17 00:00:00 2001 From: Julien Date: Thu, 2 Feb 2017 13:27:41 -0800 Subject: [PATCH 69/72] radix sort --- src/radix_sort.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/radix_sort.py b/src/radix_sort.py index 374cf9f..3c9e015 100644 --- a/src/radix_sort.py +++ b/src/radix_sort.py @@ -5,12 +5,26 @@ def radix_sort(iter): """Sort a list using the radix sort method.""" number_holder = {} - for number in iter: - number_holder[number % 10] = number - return number_holder + flat_list = iter + magnitude = 1 + loop = True + while loop: + number_holder = {} + loop = False + for number in flat_list: + the_key = grab_digit(number, magnitude) + if the_key > 0: + loop = True + number_holder.setdefault(the_key, []) + number_holder[the_key].append(number) + flat_list = [] + for number in sorted(number_holder.keys()): + flat_list += number_holder[number] + magnitude *= 10 + return flat_list - - -def rounddown(x, mag): - return int(math.floor(x / mag)) * mag +def grab_digit(x, mag): + round_down = int(math.floor(x / mag)) + digit = round_down % 10 + return digit \ No newline at end of file From 2895b79618a816eaa28be552e5ec1ab7ddd798fd Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Fri, 10 Feb 2017 15:54:03 -0800 Subject: [PATCH 70/72] added radix sort to readme, including note on time complexity --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cba51d3..4a4f5dc 100755 --- a/README.md +++ b/README.md @@ -72,4 +72,9 @@ When run as a script, a timeit function runs merge_sort() on a list of 200 rando Methods include: merge_sort(a_list): Recursively divides list at midpoint, more or less. -merge(list1, list2): A helper function to do the comparisons between values. \ No newline at end of file +merge(list1, list2): A helper function to do the comparisons between values. + +##Radix Sort +Module sorts a list using the radix algorithm, breaking up numbers and ordering them by digits of the same significance. +The primary method, radix_sort(), has a helper function, grab_digit(), that takes a number and the magnitude to return the next digit to sort the nummber by. +The time complexity of radix sort is O(nk); the algorithm walks each number in the digit, walking the list n times, and also walks each digit of each number. \ No newline at end of file From a6d82563c8f8e0f724b31ac539e88b6567de6a82 Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Fri, 10 Feb 2017 15:58:02 -0800 Subject: [PATCH 71/72] Update README.md deleted merge conflict info --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 4a4f5dc..c17a78e 100755 --- a/README.md +++ b/README.md @@ -2,10 +2,6 @@ # Data Structures ======= -[![Build Status](https://travis-ci.org/julienawilson/data-structures.svg?branch=bst)](https://travis-ci.org/julienawilson/data-structures) - -# data-structures ->>>>>>> 2f8af436a8271e1a77876030b065993c23274041 Patrick Saunders and Julien Wilson
Data Structures created in Python401 @@ -77,4 +73,4 @@ merge(list1, list2): A helper function to do the comparisons between values. ##Radix Sort Module sorts a list using the radix algorithm, breaking up numbers and ordering them by digits of the same significance. The primary method, radix_sort(), has a helper function, grab_digit(), that takes a number and the magnitude to return the next digit to sort the nummber by. -The time complexity of radix sort is O(nk); the algorithm walks each number in the digit, walking the list n times, and also walks each digit of each number. \ No newline at end of file +The time complexity of radix sort is O(nk); the algorithm walks each number in the digit, walking the list n times, and also walks each digit of each number. From 3904833eadc753187c5061e3f8f7289e780da3bf Mon Sep 17 00:00:00 2001 From: Rick Valenzuela Date: Fri, 10 Feb 2017 15:59:11 -0800 Subject: [PATCH 72/72] Update README.md edited radix entry --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c17a78e..142a68b 100755 --- a/README.md +++ b/README.md @@ -73,4 +73,4 @@ merge(list1, list2): A helper function to do the comparisons between values. ##Radix Sort Module sorts a list using the radix algorithm, breaking up numbers and ordering them by digits of the same significance. The primary method, radix_sort(), has a helper function, grab_digit(), that takes a number and the magnitude to return the next digit to sort the nummber by. -The time complexity of radix sort is O(nk); the algorithm walks each number in the digit, walking the list n times, and also walks each digit of each number. +The time complexity of radix sort is O(nk); the algorithm walks each number in the list, or n times, and also walks each digit of each number.