Skip to content

Commit

Permalink
binary trees, generic tree
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidLeoni committed Dec 7, 2018
1 parent b997262 commit 883a29d
Show file tree
Hide file tree
Showing 8 changed files with 1,197 additions and 399 deletions.
93 changes: 93 additions & 0 deletions exercises/trees/bin_tree_solution.py
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


224 changes: 224 additions & 0 deletions exercises/trees/bin_tree_test.py
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)

0 comments on commit 883a29d

Please sign in to comment.