Trees
Poll: What's wrong with this recursive function?
def find_max(nums: list[int], index: int = 0) -> int:
"""Returns the maximum of the numbers in nums, starting from the given index"""
if index == len(nums) - 1:
return nums[index]
else:
max_of_rest: int = find_max(nums, index)
if nums[index] < max_of_rest:
return max_of_rest
else:
return nums[index]
- It's missing a base case
- It's missing a recursive case
- The recursive case doesn't progress towards the base case
- It has an "off-by-one bug"
Poll: What's wrong with this recursive function?
def countdown(n: int) -> None:
"""Prints numbers from n down to 0, one on each line"""
if n > 0:
print(n)
countdown(n - 1)
- It's missing a base case
- It's missing a recursive case
- The recursive case doesn't progress towards the base case
- It has an "off-by-one bug"
Trees
It's a tree!
You may have seen trees like this decision tree:
Some tree terminology / rules:
- Each node may have data
- There is a root node (the start)
- Each node points to any number of child nodes
- Cycles are not permitted
- There are any number of leaf nodes (node with no children)
In computer science, we draw our trees with the root at the top and the leaves at the bottom.
Search Algorithms
Let's use recursion to search for an element in a tree. (See slides at the end for images of the tree)
class Tree(Generic[T]):
def __init__(self, root_data: Optional[T] = None) -> None:
if root_data is None:
self.root: Optional[Node[T]] = None
else:
self.root = Node[T](root_data)
def __str__(self) -> str:
return self.root.__str__()
def __contains__(self, item: T) -> bool:
return self.contains(item, self.root)
def contains(self, item: T, node: Optional[Node[T]]) -> bool:
if node is None:
return False
elif node.data == item:
return True
else:
return self.contains(item, node.left) or self.contains(item, node.right)
tree: Tree[str] = Tree[str]('Entry way')
assert tree.root is not None
tree.root.left = Node[str]('Living room')
tree.root.left.right = Node[str]('Kitchen')
print('Kitchen' in tree) # True
print('Bathroom' in tree) # False
The algorithm we just wrote is called a Depth-First Search, since it explores "deep" in one area before moving on to other unexplored "shallow" places (closer to the root).
Poll: This is pseudocode for a Depth-First Search on a graph that is not a tree (because it has cycles). What's a good base case?
DFS(node):
Base case:
???
Recursive case:
For each child:
Add child to explored nodes
DFS(child)
- If the node is in the set of explored nodes, do nothing
- If the node is a leaf, add it to the set of explored nodes
- If the node is
None, do nothing - If the node is
None, add it to the set of explored nodes