-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b997262
commit 883a29d
Showing
8 changed files
with
1,197 additions
and
399 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
|
||
class BinaryTree: | ||
""" A simple binary tree with left and right branches | ||
""" | ||
|
||
def __init__(self, data): | ||
self._data = data | ||
self._left = None | ||
self._right = None | ||
|
||
def data(self): | ||
return self._data | ||
|
||
def left(self): | ||
return self._left | ||
|
||
def right(self): | ||
return self._right | ||
|
||
|
||
def insert_left(self, data): | ||
""" Takes as input DATA (*NOT* a node !!) and MODIFIES current node this way: | ||
- First creates a new BinaryTree (let's call it B) into which provided data is wrapped. | ||
- Then: | ||
- if there is no left node in self, new node B is attached to the left of self | ||
- if there already is a left node L, it is substituted by new node B, and L becomes the | ||
left node of B | ||
""" | ||
#jupman-raise | ||
B = BinaryTree(data) | ||
if self._left == None: | ||
self._left = B | ||
else: | ||
B._left = self._left | ||
self._left = B | ||
#/jupman-raise | ||
|
||
def insert_right(self, data): | ||
""" Takes as input DATA (*NOT* a node !!) and MODIFIES current node this way: | ||
- First creates a new BinaryTree (let's call it B) into which provided data is wrapped. | ||
- Then: | ||
- if there is no right node in self, new node B is attached to the right of self | ||
- if there already is a right node L, it is substituted by new node B, and L becomes the | ||
right node of B | ||
""" | ||
#jupman-raise | ||
B = BinaryTree(data) | ||
if self._right == None: | ||
self._right = B | ||
else: | ||
B._right = self._right | ||
self._right = B | ||
#/jupman-raise | ||
|
||
def height_rec(self): | ||
""" RETURN an integer which is the height of the tree | ||
- implement it as recursive call which does NOT modify the tree | ||
NOTE: with big trees a recursive solution would surely exceed the call stack, | ||
but here we don't mind | ||
- A tree with only one node has height zero. | ||
""" | ||
#jupman-raise | ||
if self.left() != None: | ||
h_left = self.left().height() | ||
if self.right() != None: | ||
h_right = self.right().height() | ||
|
||
return max(h_left, h_right) + 1 | ||
#/jupman-raise | ||
|
||
def depth_dfs(self, level): | ||
""" | ||
- MODIFIES the tree by putting in the data field the value level + 1, | ||
and recursively calls itself on left and right nodes (if present) passing level + 1 | ||
- implement it as a recursive Depth First Search (DFS) traversal | ||
NOTE: with big trees a recursive solution would surely exceed the call stack, | ||
but here we don't mind | ||
- The root of a tree has depth zero. | ||
""" | ||
#jupman-raise | ||
self._data = level + 1 | ||
if self.left() != None: | ||
self.left().depth_dfs(level + 1) | ||
if self.right() != None: | ||
self.right().depth_dfs(level + 1) | ||
#/jupman-raise | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
from bin_tree_solution import * | ||
import unittest | ||
|
||
|
||
def bt(*args): | ||
""" Shorthand function that returns a BinaryTree containing the provided | ||
data and children. First parameter is the data, the following ones are the children. | ||
Usage examples: | ||
>>> print bt('a') | ||
a | ||
>>> print bt('a', bt('b'), bt('c')) | ||
a | ||
├b | ||
└c | ||
""" | ||
if (len(args) == 0): | ||
raise Exception("You need to provide at least one argument for the data!") | ||
if (len(args) > 3): | ||
raise Exception("You must provide at most two nodes ! Found instead: %s " % len(args) - 1) | ||
|
||
data = args[0] | ||
children = args[1:] | ||
|
||
ret = BinaryTree(data) | ||
|
||
cs = list(reversed(children)) | ||
if len(cs) > 0: | ||
ret._left = cs[0] | ||
if len(cs) == 2: | ||
ret._right = cs[1] | ||
return ret | ||
|
||
|
||
|
||
def str_btrees(bt1, bt2, error_row=-1): | ||
""" Returns a string version of the two trees side by side | ||
If error_row is given, the line in error is marked. | ||
If error_row == -1 it is ignored | ||
""" | ||
|
||
s1 = str(bt1) | ||
s2 = str(bt2) | ||
|
||
lines1 = s1.split("\n") | ||
lines2 = s2.split("\n") | ||
|
||
max_len1 = 0 | ||
for line in lines1: | ||
max_len1 = max(len(line.rstrip()), max_len1) | ||
max_len1 = max(max_len1, len("ACTUAL")) | ||
|
||
max_len2 = len("EXPECTED") | ||
for line in lines2: | ||
max_len2 = max(len(line.rstrip()), max_len2) | ||
|
||
strings = [] | ||
|
||
dist = 2 | ||
|
||
strings.append(("ACTUAL").ljust(max_len1 + dist)) | ||
strings.append(" EXPECTED\n") | ||
|
||
i = 0 | ||
|
||
while i < len(lines1) or i < len(lines2): | ||
|
||
if i < len(lines1): | ||
strings.append(lines1[i].rstrip()) | ||
len1 = len(lines1[i].rstrip()) | ||
else: | ||
len1 = 0 | ||
|
||
if (i < len(lines2)): | ||
len2 = len(lines2[i].rstrip()) | ||
|
||
pad_len1 = 4 + max_len1 - len1 | ||
strings.append((" " * pad_len1) + lines2[i].rstrip()) | ||
else: | ||
len2 = 0 | ||
|
||
if (error_row == i): | ||
pad_len2 = 2 + max_len1 + max_len2 - len1 - len2 | ||
strings.append((" " * pad_len2) + "<--- DIFFERENT ! ") # TODO this shoots 'DIFFERENT' too far ! | ||
|
||
strings.append("\n") | ||
|
||
i += 1 | ||
|
||
return "".join(strings) | ||
|
||
|
||
class BinaryTreeTest(unittest.TestCase): | ||
|
||
|
||
def assertTreeEqual(self, actual, expected): | ||
""" Asserts the trees actual and expected are equal """ | ||
|
||
def get_children(bt): | ||
ret = [] | ||
if bt._left: | ||
ret.append(bt._left) | ||
if bt._right: | ||
ret.append(bt._right) | ||
return ret | ||
|
||
def rec_assert(c1, c2, row): | ||
|
||
if c2 == None: | ||
raise Exception("Bad test code! Found a None node in EXPECTED tree!\n\n" | ||
+ str_btrees(actual,expected,row)) | ||
|
||
if c1 == None: | ||
raise Exception("Found a None node in actual tree! \n\n" | ||
+ str_btrees(actual,expected,row)) | ||
|
||
if not isinstance(c2, BinaryTree): | ||
raise Exception("Bad test code! EXPECTED value is an instance of %s , which is not a BinaryTree !\n\n%s" % (type(c2).__name__ , str_btrees(actual,expected,row))) | ||
|
||
if not isinstance(c1, BinaryTree): | ||
raise Exception("ACTUAL node is an instance of %s , which is not a BinaryTree !\n\n%s" | ||
% (type(c1).__name__, str_btrees(actual, expected, row ))) | ||
|
||
if c1.data() != c2.data(): | ||
raise Exception("Actual data is different from expected!\n\n" | ||
+ str_btrees(actual,expected,row)) | ||
|
||
i = 0 | ||
|
||
cs1 = get_children(c1) | ||
cs2 = get_children(c2) | ||
if (len(cs1) != len(cs2)): | ||
raise Exception("Children sizes are different !\n\n" | ||
+ str_btrees(actual, expected, row + min(len(cs1), len(cs2))) ) | ||
while (i < len(cs1) ): | ||
rec_assert(cs1[i], cs2[i], row + 1) | ||
i += 1 | ||
|
||
rec_assert(actual, expected, 0) | ||
|
||
|
||
|
||
|
||
class BinaryTreeTestTest(BinaryTreeTest): | ||
""" Tests the test itself ... """ | ||
|
||
def test_str_btrees(self): | ||
self.assertTrue('a' in str_btrees(bt('a'), bt('b'))) | ||
self.assertTrue('b' in str_btrees(bt('a'), bt('b'))) | ||
|
||
self.assertTrue('a' in str_btrees(bt('a', bt('b')), bt('b', bt('c')))) | ||
self.assertTrue('c' in str_btrees(bt('a', bt('b')), bt('b', bt('c')))) | ||
|
||
def test_assert_tree_equal(self): | ||
self.assertTreeEqual(bt('a'), bt('a')) | ||
self.assertTreeEqual(bt('a', bt('b')), bt('a', bt('b'))) | ||
|
||
with self.assertRaises(Exception): | ||
self.assertTreeEqual(bt('a'), bt('b')) | ||
with self.assertRaises(Exception): | ||
self.assertTreeEqual(bt('a', bt('b')), bt('a', bt('c'))) | ||
|
||
# different structure | ||
with self.assertRaises(Exception): | ||
self.assertTreeEqual(bt('a', bt('b')), bt('a', bt('b',bt('c')))) | ||
|
||
with self.assertRaises(Exception): | ||
self.assertTreeEqual(bt('a', bt('b',bt('c'))), bt('a', bt('b'))) | ||
|
||
def test_print(self): | ||
# self.assertTreeEqual(bt('a', bt('b', bt('v')), bt('b', bt('v'))), bt('a', bt('b', bt('v'), bt('b', bt('v'))), bt('b'))) | ||
return None | ||
|
||
|
||
class InsertLeftTest(BinaryTreeTest): | ||
|
||
def test_insert_left(self): | ||
ta = BinaryTree('a') | ||
self.assertEqual(ta.left(), None) | ||
self.assertEqual(ta.right(), None) | ||
ret = ta.insert_left('c') | ||
self.assertEqual(ret, None) | ||
tc = ta.left() | ||
self.assertEqual(tc.data(), 'c') | ||
self.assertEqual(tc.left(), None) | ||
self.assertEqual(tc.right(), None) | ||
self.assertEqual(ta.right(), None) | ||
|
||
ta.insert_left('b') | ||
tb = ta.left() | ||
self.assertEqual(ta.right(), None) | ||
self.assertEqual(tb.data(), 'b') | ||
self.assertEqual(tb.left(), tc) | ||
self.assertEqual(tb.right(), None) | ||
self.assertEqual(tc.left(), None) | ||
self.assertEqual(tc.right(), None) | ||
|
||
class InsertRightTest(BinaryTreeTest): | ||
|
||
|
||
def test_insert_right(self): | ||
ta = BinaryTree('a') | ||
self.assertEqual(ta.left(), None) | ||
self.assertEqual(ta.right(), None) | ||
ret = ta.insert_right('c') | ||
self.assertEqual(ret, None) | ||
tc = ta.right() | ||
self.assertEqual(tc.data(), 'c') | ||
self.assertEqual(tc.left(), None) | ||
self.assertEqual(tc.right(), None) | ||
self.assertEqual(ta.left(), None) | ||
|
||
ta.insert_right('b') | ||
tb = ta.right() | ||
self.assertEqual(ta.left(), None) | ||
self.assertEqual(tb.data(), 'b') | ||
self.assertEqual(tb.left(), None) | ||
self.assertEqual(tb.right(), tc) | ||
self.assertEqual(tc.left(), None) | ||
self.assertEqual(tc.right(), None) |
Oops, something went wrong.