Mutable Trees
Tuesday, 7/21
Announcements
Review: Linked List
Define a function link_extend that extends a linked list mutatively. It takes in two linked lists, s1 and s2.
It should add a copy of s2 to the end of s1, such that if any changes to s2 are made after, s1 will not change.
def link_extend(s1, s2):� """
Mutatively extend s1 with s2
>>> s1 = Link(1)
>>> s2 = Link(2)
>>> link_extend(s1, s2)
>>> s2.rest = Link(3)
>>> print_link(s1)
<1 2>
"""
class Link:� empty = ()�� def __init__(self, first, rest=empty):� assert rest is Link.empty or isinstance(rest, Link)� self.first = first� self.rest = rest
Review: Linked List
def link_extend(s1, s2):� """
Mutatively extend s1 with s2
>>> s1 = Link(1)
>>> s2 = Link(2)
>>> link_extend(s1, s2)
>>> s2.rest = Link(3)
>>> print_link(s1)
<1 2>
"""
if s2 is Link.empty:
return
elif s1.rest is Link.empty:
s1.rest = Link(s2.first)
link_extend(s1.rest, s2.rest)
else:
link_extend(s1.rest, s2)
Review: Linked List
def link_extend(s1, s2):� """
Mutatively extend s1 with s2
>>> s1 = Link(1)
>>> s2 = Link(2)
>>> link_extend(s1, s2)
>>> s2.rest = Link(3)
>>> print_link(s1)
<1 2>
"""
while s1.rest is not Link.empty:
s1 = s1.rest
while s2 is not Link.empty:
s1.rest = Link(s2.first)
s1 = s1.rest
s2 = s2.rest
Trees
Tree class: Terminology
9
4
5
2
8
3
6
1
9
node
entry
root
subtree
subtree
subtree
leaf
leaf
leaf
leaf
Tree class
class Tree:
def __init__(self, entry, subtrees=[]):
self.entry = entry
self.subtrees = list(subtrees)
def is_leaf(self):
return not self.subtrees
def tree(entry, subtrees=[]):
...
def entry(t):
...
def subtrees(t):
...
def is_leaf(t):
return not subtrees(t)
3
4
2
1
>>> t = Tree(3, [Tree(2, [Tree(1)]), Tree(4)])
>>> t.entry
3
>>> t.subtrees[0].entry
2
>>> t.subtrees[0].subtrees[0].is_leaf()
True
Tree class: Verifying inputs
class Tree:
def __init__(self, entry, subtrees):
for s in subtrees:
assert isinstance(s, Tree), 'each subtree must be a Tree'
self.entry = entry
self.subtrees = list(subtrees)
Each subtree must be a Tree
>>> t = Tree(1, [2, 3])
AssertionError
>>> t = Tree(1, [Tree(2), 3])
AssertionError
>>> t = Tree(1)
Trees
Mutation
>>> t.subtrees.append(Tree(4))
>>> t.entry = 3
>>> t = Tree(1, [Tree(2)])
4
1
2
3
Trees
tree_contains: No mutation
def tree_contains(t, e):
"*** YOUR CODE HERE ***"
tree_contains: Base case
def tree_contains(t, e):
"*** YOUR CODE HERE ***"
if e == t.entry:
return True
tree_contains: Base case
def tree_contains(t, e):
"*** YOUR CODE HERE ***"
if e == t.entry:
return True
elif t.is_leaf():
return False
tree_contains: Base case
def tree_contains(t, e):
"*** YOUR CODE HERE ***"
if e == t.entry:
return True
elif t.is_leaf():
return False
9
4
5
2
8
3
6
1
tree_contains: Base case
def tree_contains(t, e):
"*** YOUR CODE HERE ***"
if e == t.entry:
return True
elif t.is_leaf():
return False
else:
for s in t.subtrees:
if tree_contains(s, e):
return True
return False
9
4
5
2
8
3
6
1
tree_contains: Using list comprehensions
def tree_contains(t, e):
"*** YOUR CODE HERE ***"
if e == t.entry:
return True
elif t.is_leaf():
return False
else:
return any([tree_contains(s, e)
for s in
t.subtrees])
def tree_contains(t, e):
"*** YOUR CODE HERE ***"
if e == t.entry:
return True
elif t.is_leaf():
return False
else:
for s in t.subtrees:
if tree_contains(s, e):
return True
return False
tree_contains: Even more concise!
def tree_contains(t, e):
"*** YOUR CODE HERE ***"
if e == t.entry:
return True
elif t.is_leaf():
return False
else:
return any([tree_contains(s, e)
for s in
t.subtrees])
def tree_contains(t, e):
"*** YOUR CODE HERE ***"
return e == t.entry or \
any([tree_contains(s, e)
for s in
t.subtrees])
tree_contains: Runtime
O(1)
O(1)
O(1)
O(1)
O(1)
O(1)
O(1)
O(1)
Trees
tree_map
def tree_map(t, fn):
"*** YOUR CODE HERE ***"
tree_map
def tree_map(t, fn):
"*** YOUR CODE HERE ***"
if t.is_leaf():
t.entry = fn(t.entry)
tree_map
def tree_map(t, fn):
"*** YOUR CODE HERE ***"
if t.is_leaf():
t.entry = fn(t.entry)
tree_map
def tree_map(t, fn):
"*** YOUR CODE HERE ***"
if t.is_leaf():
t.entry = fn(t.entry)
else:
t.entry = fn(t.entry)
for s in t.subtrees:
tree_map(s, fn)
tree_map
def tree_map(t, fn):
"*** YOUR CODE HERE ***"
if t.is_leaf():
t.entry = fn(t.entry)
else:
t.entry = fn(t.entry)
for s in t.subtrees:
tree_map(s, fn)
def tree_map(t, fn):
"*** YOUR CODE HERE ***"
t.entry = fn(t.entry)
for s in t.subtrees:
tree_map(s, fn)
tree_map
O(1)
O(1)
O(1)
O(1)
O(1)
O(1)
O(1)
O(1)
Trees
Binary trees: Definition
9
4
5
2
8
3
6
1
Not binary tree
5
3
2
7
4
8
1
Binary tree
5
2
1
7
4
8
3
Binary tree
Binary trees: Class
class BinaryTree:
empty = ()
def __init__(self, entry, left=empty, right=empty):
self.entry = entry
self.left = left
self.right = right
BinaryTree(3,
BinaryTree(2,
BinaryTree(1)),
BinaryTree(4,
BinaryTree.empty,
BinaryTree(3)))
Trees
Binary trees: Definition
9
4
5
2
8
3
6
1
Not binary search tree
5
3
2
7
4
8
1
Not binary search tree
5
2
1
7
4
8
3
Binary search tree
Trees
bst_contains
def bst_contains(t, e):
"*** YOUR CODE HERE ***"
bst_contains: Base case
def bst_contains(t, e):
"*** YOUR CODE HERE ***"
if e == t.entry:
return True
bst_contains: Recursive cases
def bst_contains(t, e):
"*** YOUR CODE HERE ***"
if e == t.entry:
return True
elif e < t.entry:
return bst_contains(t.left, e)
else:
return bst_contains(t.right, e)
5
2
1
7
4
8
3
bst_contains: Base case (revisited)
def bst_contains(t, e):
"*** YOUR CODE HERE ***"
if t is BinaryTree.empty:
return False
elif e == t.entry:
return True
elif e < t.entry:
return bst_contains(t.left, e)
else:
return bst_contains(t.right, e)
5
7
8
bst_contains: Runtime
5
2
1
7
4
8
6
5
3
4
6