Skip to content

Commit e9798c3

Browse files
author
Unknown
committed
merge
1 parent 53fcf3d commit e9798c3

File tree

5 files changed

+409
-1
lines changed

5 files changed

+409
-1
lines changed

Class9.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
## Class 9: Trees & Binary Search Trees
2+
3+
### Topics
4+
- [Tree] data structure, [terminology]
5+
- [Binary search tree], [operations]
6+
7+
### Resources
8+
- Review Make School's [trees slides]
9+
- Watch Make School's [trees video lecture]
10+
- Read Interview Cake's [logarithms and binary search article][IC logarithms] and [binary tree properties article][IC binary tree]
11+
- Watch HackerRank's [trees and binary search tree video][HR trees video] (up to 3:00)
12+
- Watch Harvards's [family trees and binary search tree video][Harvard trees video]
13+
- Play with VisuAlgo's [interactive binary search tree visualization][visualgo bst]
14+
15+
### Challenges
16+
- Implement `BinaryTreeNode` class with the following properties and instance methods using [binary tree starter code]:
17+
- `data` - the node's data element
18+
- `left` - the node's left child, if any
19+
- `right` - the node's right child, if any
20+
- `is_leaf` - check if the node is a leaf (has no children)
21+
- `is_branch` - check if the node is internal (has at least one child)
22+
- `height` - return the height of the node (the number of edges on the longest downward path from the node to a descendant leaf node)
23+
- Implement `BinarySearchTree` class using `BinaryTreeNode` objects with the following properties and instance methods using [binary tree starter code]:
24+
- `size` - property that tracks the number of nodes in constant time
25+
- `is_empty` - check if the tree is empty (has no nodes)
26+
- `height` - return the height of the tree (the number of edges on the longest downward path from the tree's root node to a descendant leaf node)
27+
- `contains(item)` - return a boolean indicating whether `item` is present in the tree
28+
- `search(item)` - return an item in the tree matching the given `item`, or `None` if not found
29+
- `insert(item)` - insert the given `item` in order into the tree
30+
- `_find_node(item)` - return the node containing `item` in the tree, or `None` if not found (*hint: implement this first*)
31+
- `_find_parent_node(item)` - return the parent of the node containing `item` (or the parent of where `item` would be if inserted) in the tree, or `None` if the tree is empty or has only a root node
32+
- Run `pytest binarytree_test.py` to run the [binary tree unit tests] and fix any failures
33+
- Write additional unit tests for the `BinaryTreeNode` and `BinarySearchTree` classes
34+
- Add to existing test cases to ensure the `size` property is correct
35+
- Include test cases for the `height` instance method on both classes
36+
- Annotate class instance methods with complexity analysis of running time
37+
38+
### Stretch Challenges
39+
- Implement this additional `BinarySearchTree` class instance method:
40+
- `delete(item)` - remove the node containing `item` from the tree
41+
- Implement binary search tree with singly linked list nodes (having only one link to another node) instead of binary tree nodes (having two links to other nodes)
42+
43+
44+
[tree]: https://en.wikipedia.org/wiki/Tree_(data_structure)
45+
[terminology]: https://en.wikipedia.org/wiki/Tree_(data_structure)#Terminology_used_in_trees
46+
[binary search tree]: https://en.wikipedia.org/wiki/Binary_search_tree
47+
[operations]: https://en.wikipedia.org/wiki/Binary_search_tree#Operations
48+
49+
[trees slides]: slides/Trees.pdf
50+
[trees video lecture]: https://www.youtube.com/watch?v=Yr3y78d2KYI
51+
[HR trees video]: https://www.youtube.com/watch?v=oSWTXtMglKE
52+
[HR bst interview problem]: https://www.youtube.com/watch?v=i_Q0v_Ct5lY
53+
[Harvard trees video]: https://www.youtube.com/watch?v=mFptHjTT3l8
54+
[IC logarithms]: https://www.interviewcake.com/article/python/logarithms
55+
[IC binary tree]: https://www.interviewcake.com/concept/python/binary-tree
56+
[visualgo bst]: https://visualgo.net/bst
57+
58+
[binary tree starter code]: source/binarytree.py
59+
[binary tree unit tests]: source/binarytree_test.py

ReadMe.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
| 6 | Friday, November 3 | [Call Routing Project](Class6.md) |
1818
| 7 | Monday, November 6 | [Maps & Hash Tables](Class7.md) |
1919
| 8 | Wednesday, November 8 | [Sets & Circular Buffers](Class8.md) |
20-
| 9 | Friday, November 10 | Trees |
20+
| 9 | Friday, November 10 | [Trees & Binary Search Trees](Class9.md) |
2121
| 10 | Monday, November 13 | Tree Traversals |
2222
| 11 | Wednesday, November 15 | Iterative Sorting Algorithms |
2323
| 12 | Friday, November 17 | Integer Sorting Algorithms |

slides/Trees.pdf

232 KB
Binary file not shown.

source/binarytree.py

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
#!python
2+
3+
4+
class BinaryTreeNode(object):
5+
6+
def __init__(self, data):
7+
"""Initialize this binary tree node with the given data."""
8+
self.data = data
9+
self.left = None
10+
self.right = None
11+
12+
def __repr__(self):
13+
"""Return a string representation of this binary tree node."""
14+
return 'BinaryTreeNode({!r})'.format(self.data)
15+
16+
def is_leaf(self):
17+
"""Return True if this node is a leaf (has no children)."""
18+
# TODO: Check if both left child and right child have no value
19+
return ... and ...
20+
21+
def is_branch(self):
22+
"""Return True if this node is a branch (has at least one child)."""
23+
# TODO: Check if either left child or right child has a value
24+
return ... or ...
25+
26+
def height(self):
27+
"""Return the height of this node (the number of edges on the longest
28+
downward path from this node to a descendant leaf node).
29+
TODO: Best and worst case running time: ??? under what conditions?"""
30+
# TODO: Check if left child has a value and if so calculate its height
31+
...
32+
# TODO: Check if right child has a value and if so calculate its height
33+
...
34+
# Return one more than the greater of the left height and right height
35+
...
36+
37+
38+
class BinarySearchTree(object):
39+
40+
def __init__(self, items=None):
41+
"""Initialize this binary search tree and insert the given items."""
42+
self.root = None
43+
self.size = 0
44+
if items is not None:
45+
for item in items:
46+
self.insert(item)
47+
48+
def __repr__(self):
49+
"""Return a string representation of this binary search tree."""
50+
return 'BinarySearchTree({} nodes)'.format(self.size)
51+
52+
def is_empty(self):
53+
"""Return True if this binary search tree is empty (has no nodes)."""
54+
return self.root is None
55+
56+
def height(self):
57+
"""Return the height of this tree (the number of edges on the longest
58+
downward path from this tree's root node to a descendant leaf node).
59+
TODO: Best and worst case running time: ??? under what conditions?"""
60+
# TODO: Check if root node has a value and if so calculate its height
61+
...
62+
63+
def contains(self, item):
64+
"""Return True if this binary search tree contains the given item.
65+
TODO: Best case running time: ??? under what conditions?
66+
TODO: Worst case running time: ??? under what conditions?"""
67+
# Find a node with the given item, if any
68+
node = self._find_node(item)
69+
# Return True if a node was found, or False
70+
return node is not None
71+
72+
def search(self, item):
73+
"""Return an item in this binary search tree matching the given item,
74+
or None if the given item is not found.
75+
TODO: Best case running time: ??? under what conditions?
76+
TODO: Worst case running time: ??? under what conditions?"""
77+
# Find a node with the given item, if any
78+
node = self._find_node(item)
79+
# TODO: Return the node's data if found, or None
80+
return node.data if ... else None
81+
82+
def insert(self, item):
83+
"""Insert the given item in order into this binary search tree.
84+
TODO: Best case running time: ??? under what conditions?
85+
TODO: Worst case running time: ??? under what conditions?"""
86+
# Handle the case where the tree is empty
87+
if self.is_empty():
88+
# TODO: Create a new root node
89+
self.root = ...
90+
# TODO: Increase the tree size
91+
self.size ...
92+
return
93+
# Find the parent node of where the given item should be inserted
94+
parent = self._find_parent_node(item)
95+
# TODO: Check if the given item should be inserted left of parent node
96+
if ...:
97+
# TODO: Create a new node and set the parent's left child
98+
parent.left = ...
99+
# TODO: Check if the given item should be inserted right of parent node
100+
elif ...:
101+
# TODO: Create a new node and set the parent's right child
102+
parent.right = ...
103+
# TODO: Increase the tree size
104+
self.size ...
105+
106+
def _find_node(self, item):
107+
"""Return the node containing the given item in this binary search tree,
108+
or None if the given item is not found.
109+
TODO: Best case running time: ??? under what conditions?
110+
TODO: Worst case running time: ??? under what conditions?"""
111+
# Start with the root node
112+
node = self.root
113+
# Loop until we descend past the closest leaf node
114+
while node is not None:
115+
# TODO: Check if the given item matches the node's data
116+
if ...:
117+
# Return the found node
118+
return node
119+
# TODO: Check if the given item is less than the node's data
120+
elif ...:
121+
# TODO: Descend to the node's left child
122+
node = ...
123+
# TODO: Check if the given item is greater than the node's data
124+
elif ...:
125+
# TODO: Descend to the node's right child
126+
node = ...
127+
# Not found
128+
return None
129+
130+
def _find_parent_node(self, item):
131+
"""Return the parent node of the node containing the given item
132+
(or the parent node of where the given item would be if inserted)
133+
in this tree, or None if this tree is empty or has only a root node.
134+
TODO: Best case running time: ??? under what conditions?
135+
TODO: Worst case running time: ??? under what conditions?"""
136+
# Start with the root node and keep track of its parent
137+
node = self.root
138+
parent = None
139+
# Loop until we descend past the closest leaf node
140+
while node is not None:
141+
# TODO: Check if the given item matches the node's data
142+
if ...:
143+
# Return the parent of the found node
144+
return parent
145+
# TODO: Check if the given item is less than the node's data
146+
elif ...:
147+
# TODO: Update the parent and descend to the node's left child
148+
parent = node
149+
node = ...
150+
# TODO: Check if the given item is greater than the node's data
151+
elif ...:
152+
# TODO: Update the parent and descend to the node's right child
153+
parent = node
154+
node = ...
155+
# Not found
156+
return parent
157+
158+
# This space intentionally left blank (please do not delete this comment)
159+
160+
161+
def test_binary_search_tree():
162+
# Create a complete binary search tree of 3, 7, or 15 items in level-order
163+
# items = [2, 1, 3]
164+
items = [4, 2, 6, 1, 3, 5, 7]
165+
# items = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15]
166+
print('items: {}'.format(items))
167+
168+
tree = BinarySearchTree()
169+
print('tree: {}'.format(tree))
170+
print('root: {}'.format(tree.root))
171+
172+
print('\nInserting items:')
173+
for item in items:
174+
tree.insert(item)
175+
print('insert({}), size: {}'.format(item, tree.size))
176+
print('root: {}'.format(tree.root))
177+
178+
print('\nSearching for items:')
179+
for item in items:
180+
result = tree.search(item)
181+
print('search({}): {}'.format(item, result))
182+
item = 123
183+
result = tree.search(item)
184+
print('search({}): {}'.format(item, result))
185+
186+
187+
if __name__ == '__main__':
188+
test_binary_search_tree()

0 commit comments

Comments
 (0)