-
Notifications
You must be signed in to change notification settings - Fork 0
Bst #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Bst #11
Changes from all commits
b9f3ad4
5c44fa0
f910750
588ca91
e56c299
5e8601b
25bc1d1
1e1b34e
8a43f41
f43ea6e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| #!/usr/bin/env python | ||
| import random | ||
| import subprocess | ||
|
|
||
|
|
||
| class Bst(object): | ||
| """Binary Search Tree using a dictionary""" | ||
| def __init__(self, value=None): | ||
| self.tree = {} | ||
| self.top = None | ||
| self._size = 0 | ||
| self._depth = 0 | ||
|
|
||
| if value is not None: | ||
| self.tree[value] = {'depth': 1} | ||
| self.top = value | ||
| self._size += 1 | ||
| self._depth += 1 | ||
|
|
||
| def insert(self, value): | ||
| """Insert a node with value in order. | ||
|
|
||
| In value is already present, it is ignored.""" | ||
| current = self.top | ||
| if current is None: | ||
| self.tree[value] = {'depth': 1} | ||
| self.top = value | ||
| self._size += 1 | ||
| self._depth += 1 | ||
| return | ||
| depth = 1 # starts trying to insert at depth 2 | ||
| while current is not None: | ||
| depth += 1 | ||
| if current == value: | ||
| return | ||
| if current < value: | ||
| traverse = self.tree[current].get('right') | ||
| child = 'right' | ||
| else: | ||
| traverse = self.tree[current].get('left') | ||
| child = 'left' | ||
| if traverse is None: | ||
| #actual insert | ||
| self.tree[value] = {'depth': depth} | ||
| self.tree[current][child] = value | ||
| self._size += 1 | ||
| if depth > self._depth: | ||
| self._depth = depth | ||
| return | ||
| current = traverse | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given the upcoming need to traverse, you might consider breaking the code responsible for determining where to traverse to into a support method. |
||
|
|
||
|
|
||
| def balance(self): | ||
| """Returns the balance of the tree: | ||
|
|
||
| Returns a positive representing how much deeper the tree is on the | ||
| right site or negative if the left is longer. | ||
| Return 0 if the tree is balanced (same depth on both sides) | ||
|
|
||
| """ | ||
| left_deep = 0 | ||
| right_deep = 0 | ||
| for node, v in self.tree.items(): | ||
| if self.top > node: | ||
| if left_deep < v['depth']: | ||
| left_deep = v['depth'] | ||
| elif self.top < node: | ||
| if right_deep < v['depth']: | ||
| right_deep = v['depth'] | ||
| return right_deep - left_deep | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. better than what we saw in class today. Still O(n) though. |
||
|
|
||
| def contains(self, value): | ||
| """Returns true if value is in the tree.""" | ||
| return value in self.tree | ||
|
|
||
| def size(self): | ||
| """Returns the number of nodes in the tree.""" | ||
| return self._size | ||
|
|
||
| def depth(self): | ||
| """Returns the depth of the tree.""" | ||
| return self._depth | ||
|
|
||
| def get_dot(self): | ||
| """return the tree with root 'self' as a dot graph for visualization""" | ||
| return "digraph G{\n%s}" % ("" if not self.tree else ( | ||
| "\n%s\n" % ( | ||
| # "\t%s;\n%s\n" % ( | ||
| # list(self.tree), | ||
| "\n".join(self._get_dot(self.top)) | ||
| ) | ||
| )) | ||
|
|
||
|
|
||
| def _get_dot(self, current): | ||
| """recursively prepare a dot graph entry for this node.""" | ||
| left = self.tree[current].get('left') | ||
| right = self.tree[current].get('right') | ||
| if left is not None: | ||
| yield "\t%s -> %s;" % (current, left) | ||
| for i in self._get_dot(left): | ||
| yield i | ||
| elif right is not None: | ||
| r = random.randint(0, 1e9) | ||
| yield "\tnull%s [shape=point];" % r | ||
| yield "\t%s -> null%s;" % (current, r) | ||
| if right is not None: | ||
| yield "\t%s -> %s;" % (current, right) | ||
| for i in self._get_dot(right): | ||
| yield i | ||
| elif left is not None: | ||
| r = random.randint(0, 1e9) | ||
| yield "\tnull%s [shape=point];" % r | ||
| yield "\t%s -> null%s;" % (current, r) | ||
|
|
||
|
|
||
| def main(): | ||
| """Best case and worst case are the same.""" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. true for you. Sad though. You should try implementing the binary search algorithm at some point. |
||
| tree = Bst() | ||
| for num in reversed(range(10)): | ||
| tree.insert(num) | ||
| for num in range(10, 15): | ||
| tree.insert(num) | ||
| dot_graph = tree.get_dot() | ||
| t = subprocess.Popen(["dot", "-Tpng"], stdin=subprocess.PIPE) | ||
| t.communicate(dot_graph) | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| main() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| import bst | ||
| import pytest | ||
|
|
||
|
|
||
| def test_Bst(): | ||
| """Constructing a bst tree""" | ||
| tree = bst.Bst() | ||
| assert tree.tree == {} | ||
| tree = bst.Bst(5) | ||
| assert tree.tree == {5: {'depth': 1}} | ||
|
|
||
|
|
||
| def test_insert(empty_tree): | ||
| """Test insert into a tree""" | ||
| tree = empty_tree | ||
| tree.insert(20) | ||
| assert tree.tree == {20: {'depth': 1}} | ||
| tree.insert(30) | ||
| expect = {20: {'depth': 1, | ||
| 'right': 30}, | ||
| 30: {'depth': 2}} | ||
| assert tree.tree == expect | ||
| tree.insert(40) | ||
| expect = {20: {'depth': 1, | ||
| 'right': 30}, | ||
| 30: {'depth': 2, | ||
| 'right': 40}, | ||
| 40: {'depth': 3}} | ||
| assert tree.tree == expect | ||
| tree.insert(10) | ||
| expect = {20: {'depth': 1, | ||
| 'left': 10, | ||
| 'right': 30}, | ||
| 30: {'depth': 2, | ||
| 'right': 40}, | ||
| 40: {'depth': 3}, | ||
| 10: {'depth': 2}} | ||
| assert tree.tree == expect | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test is too long. make some support methods that do the work, then call them. Or break this test into a series of tests: |
||
|
|
||
|
|
||
| def test_size_emptree(empty_tree): | ||
| t = empty_tree | ||
| assert t.size() == 0 | ||
|
|
||
|
|
||
| def test_size_tree(filled_tree): | ||
| t = filled_tree | ||
| assert t.size() == 14 | ||
|
|
||
|
|
||
| def test_depth_emptree(empty_tree): | ||
| t = empty_tree | ||
| assert t.depth() == 0 | ||
|
|
||
|
|
||
| def test_depth_tree(filled_tree): | ||
| t = filled_tree | ||
| assert t.depth() == 10 | ||
|
|
||
| def test_balance(empty_tree, filled_tree): | ||
| t = empty_tree | ||
| assert t.balance() == 0 | ||
| t.insert(3) | ||
| assert t.balance() == 0 | ||
| t = filled_tree | ||
| assert t.balance() == -5 | ||
|
|
||
| def test_contains(filled_tree): | ||
| t = filled_tree | ||
| for num in reversed(range(10)): | ||
| assert t.contains(num) is True | ||
| for num in range(10, 14): | ||
| assert t.contains(num) is True | ||
| for num in range(20, 25): | ||
| assert t.contains(num) is False | ||
|
|
||
|
|
||
| @pytest.fixture(scope='function') | ||
| def filled_tree(): | ||
| tree = bst.Bst() | ||
| for num in reversed(range(10)): | ||
| tree.insert(num) | ||
| for num in range(10, 14): | ||
| tree.insert(num) | ||
| return tree | ||
|
|
||
|
|
||
| @pytest.fixture(scope='function') | ||
| def empty_tree(): | ||
| tree = bst.Bst() | ||
| return tree | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A dict of dicts. This is going to be interesting!