Skip to content

Commit f7d88a2

Browse files
committed
add binary tree lib
1 parent d60e2da commit f7d88a2

File tree

2 files changed

+251
-0
lines changed

2 files changed

+251
-0
lines changed

binary_tree.py

+181
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
class Node:
2+
"""
3+
Tree node: left and right child + data which can be any object
4+
"""
5+
def __init__(self, data):
6+
"""
7+
Node constructor
8+
9+
@param data node data object
10+
"""
11+
self.left = None
12+
self.right = None
13+
self.data = data
14+
15+
class BinaryTree:
16+
"""
17+
Binary tree: methods to manipulate a binary tree
18+
"""
19+
def __init__(self):
20+
"""
21+
Binary tree constructor
22+
"""
23+
self.root = None
24+
25+
def get_root(self):
26+
return self.root
27+
28+
def bt_insert(root, data):
29+
"""
30+
Insert new node with data
31+
32+
@param root tree root
33+
@param data node data object
34+
@returns inserted node
35+
"""
36+
if root == None:
37+
root = Node(data)
38+
else:
39+
if data <= root.data:
40+
root.left = bt_insert(root.left, data)
41+
else:
42+
root.right = bt_insert(root.right, data)
43+
return root
44+
45+
def bt_lookup(root, data, parent=None):
46+
"""
47+
Lookup node containing data
48+
49+
@param root tree root
50+
@param data node data object
51+
@returns node and node's parent if found or None, None
52+
"""
53+
if root == None:
54+
return None, None
55+
if data < root.data:
56+
return bt_lookup(root.left, data, root)
57+
elif data > root.data:
58+
return bt_lookup(root.right, data, root)
59+
else:
60+
return root, parent
61+
62+
def bt_compare_trees(t1, t2):
63+
"""
64+
Compare 2 trees
65+
66+
@param t1 tree 1
67+
@param t2 tree 2
68+
@returns True if the 2 trees are identical or False
69+
"""
70+
if t1 == None and t2 == None:
71+
return True
72+
elif (t1 == None and t2 != None) or (t1 != None and t2 == None):
73+
return False
74+
else:
75+
return (t1.data == t2.data) \
76+
and bt_compare_trees(t1.left, t2.left) \
77+
and bt_compare_trees(t1.right, t2.right)
78+
79+
def bt_print_tree(root):
80+
"""
81+
Print tree content in order
82+
83+
@param root tree root node
84+
"""
85+
if root == None:
86+
pass
87+
else:
88+
bt_print_tree(root.left)
89+
print root.data,
90+
bt_print_tree(root.right)
91+
92+
def bt_tree_data(node):
93+
"""
94+
Generator to get the tree nodes data
95+
96+
@param node tree root node
97+
"""
98+
# we use a stack to traverse the tree in a non-recursive way
99+
stack = []
100+
while stack or node:
101+
if node:
102+
stack.append(node)
103+
node = node.left
104+
else: # we are returning so we pop the node and we yield it
105+
node = stack.pop()
106+
yield node.data
107+
node = node.right
108+
109+
def bt_delete(root, data):
110+
"""
111+
Delete node containing data
112+
113+
@param root tree root node
114+
@param data node's content to delete
115+
"""
116+
# get node containing data
117+
node, parent = bt_lookup(root, data)
118+
if node != None:
119+
childs_count = bt_childs_count(node)
120+
if childs_count == 0:
121+
# if node has no childs, just remove it
122+
if bt_which_child(parent, node) == 'left':
123+
parent.left = None
124+
else:
125+
parent.right = None
126+
elif childs_count == 1:
127+
# if node has 1 child
128+
# replace node by its child
129+
if node.left:
130+
node.data = node.left.data
131+
node.left = None
132+
else:
133+
node.data = node.right.data
134+
node.right = None
135+
else:
136+
# if node has 2 childs
137+
# find its successor
138+
successor = node.right
139+
while successor.left:
140+
successor = successor.left
141+
node.data = successor.data
142+
# if the successor has a child, replace it with its child
143+
# it can only be a right child at this point
144+
if successor.right:
145+
successor.data = successor.right.data
146+
successor.right = None
147+
else:
148+
node.right = None
149+
150+
def bt_which_child(parent, child):
151+
"""
152+
Test if child is parent's left child or parent's right child
153+
154+
@param parent parent node
155+
@param child parent's child node
156+
"""
157+
if parent == None:
158+
return None
159+
if parent.left == child:
160+
return 'left'
161+
else:
162+
return 'right'
163+
164+
def bt_childs_count(node):
165+
"""
166+
Return the number of childs
167+
168+
@param node node to get nb of childs
169+
@returns number of childs: 0, 1, 2
170+
"""
171+
if node == None:
172+
return None
173+
cnt = 0
174+
if node.left:
175+
cnt += 1
176+
if node.right:
177+
cnt += 1
178+
return cnt
179+
180+
181+

tests/test_binary_tree.py

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import unittest
2+
import binary_tree
3+
4+
class BinaryTreeTest(unittest.TestCase):
5+
6+
def test_binary_tree(self):
7+
8+
data = [10, 5, 15, 4, 7, 13, 17, 11, 14]
9+
# create 2 trees with the same content
10+
bt = binary_tree.BinaryTree()
11+
root = binary_tree.bt_insert(bt.get_root(), data[0])
12+
for i in data[1:]:
13+
binary_tree.bt_insert(root, i)
14+
15+
bt2 = binary_tree.BinaryTree()
16+
root2 = binary_tree.bt_insert(bt2.get_root(), data[0])
17+
for i in data[1:]:
18+
binary_tree.bt_insert(root2, i)
19+
20+
# check if both trees are identical
21+
self.assertTrue(binary_tree.bt_compare_trees(root, root2))
22+
23+
# check the content of the tree inorder
24+
t = []
25+
for d in binary_tree.bt_tree_data(root):
26+
t.append(d)
27+
self.assertEquals(t, [4, 5, 7, 10, 11, 13, 14, 15, 17])
28+
29+
# test lookup
30+
node, parent = binary_tree.bt_lookup(root, 9)
31+
self.assertTrue(node == None)
32+
node, parent = binary_tree.bt_lookup(root, 11)
33+
self.assertTrue(node.data == 11)
34+
self.assertTrue(parent.data == 13)
35+
36+
# delete a leaf node
37+
binary_tree.bt_delete(root, 4)
38+
# check the content of the tree inorder
39+
t = []
40+
for d in binary_tree.bt_tree_data(root):
41+
t.append(d)
42+
self.assertEquals(t, [5, 7, 10, 11, 13, 14, 15, 17])
43+
44+
# delete a node with 1 child
45+
binary_tree.bt_delete(root, 5)
46+
# check the content of the tree inorder
47+
t = []
48+
for d in binary_tree.bt_tree_data(root):
49+
t.append(d)
50+
self.assertEquals(t, [7, 10, 11, 13, 14, 15, 17])
51+
52+
# delete a node with 2 childs
53+
binary_tree.bt_delete(root, 13)
54+
# check the content of the tree inorder
55+
t = []
56+
for d in binary_tree.bt_tree_data(root):
57+
t.append(d)
58+
self.assertEquals(t, [7, 10, 11, 14, 15, 17])
59+
60+
# delete a node with 2 childs
61+
binary_tree.bt_delete(root, 15)
62+
# check the content of the tree inorder
63+
t = []
64+
for d in binary_tree.bt_tree_data(root):
65+
t.append(d)
66+
self.assertEquals(t, [7, 10, 11, 14, 17])
67+
68+
if __name__ == '__main__':
69+
unittest.main()
70+

0 commit comments

Comments
 (0)