Unit 24: Trees
Branching structures.
Unit 24 of 31 in Python for kids. Its 5 lessons are Branches, Walking a Tree, Trees That Sort Themselves, Searching Fast and Tree Master — below is everything each one explains, and a question or two from it to try.
Every sample on this page was run through real Python before it shipped, and prints exactly what it says it prints.
This unit opens with a fortnight’s trial of everything — no card needed — or with a family plan, bought in the iPhone app. The first two units of every track are free for ever. Try it in the app.
🌳 Branches
A chain that splits
A chain node points at one other node. A tree node points at several — its children. The node at the top is the root, and nodes with no children are leaves.
Python
class Node:
def __init__(self, value):
self.value = value
self.children = []
root = Node("animals")
root.children.append(Node("cat"))
root.children.append(Node("dog"))
print(root.value)
for child in root.children:
print(" -", child.value)
It prints
animals - cat - dog
Trees are everywhere
Folders inside folders, the menu on a website, the pieces of a web page — all trees. Anything where one thing contains several others is a tree.
Python
tree = {
"school": {
"year5": {},
"year6": {"art": {}},
}
}
print(list(tree))
print(list(tree["school"]))
print(list(tree["school"]["year6"]))
It prints
['school'] ['year5', 'year6'] ['art']
Two pointers instead of a list
A binary tree allows at most two children, called left and right. That limit is what makes the clever tricks in the next lessons possible.
Python
class Node:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
root = Node(5)
root.left = Node(3)
root.right = Node(8)
print(root.value)
print(root.left.value, root.right.value)
It prints
5 3 8
Try it yourself
What is a leaf?
- The node at the top
- A node with no children
- A node with two children
- The biggest value
What does this print?
Python
class Node:
def __init__(self, v):
self.value = v
self.left = None
self.right = None
r = Node(1)
r.left = Node(2)
r.left.right = Node(3)
print(r.left.right.value)
print(r.right)
Answer them in the app
🥾 Walking a Tree
Recursion fits trees perfectly
Every branch of a tree is itself a smaller tree. So a function that handles a node just calls itself on the children — and the base case is running out of tree.
Python
class Node:
def __init__(self, v):
self.value = v
self.left = None
self.right = None
root = Node(1)
root.left = Node(2)
root.right = Node(3)
def show(node):
if node is None:
return
print(node.value)
show(node.left)
show(node.right)
show(root)
It prints
1 2 3
Where you print decides the order
Print *before* the children and you go top-down (pre-order). Print *between* them and you get in-order. Print *after* both and you get post-order. Same walk, three orders.
Python
class Node:
def __init__(self, v):
self.value = v
self.left = None
self.right = None
root = Node(2)
root.left = Node(1)
root.right = Node(3)
def in_order(node):
if node is None:
return
in_order(node.left)
print(node.value)
in_order(node.right)
in_order(root)
It prints
1 2 3
Counting and measuring
The same shape counts nodes or measures how tall the tree is. Height is 1 plus the taller of the two sides.
Python
class Node:
def __init__(self, v):
self.value = v
self.left = None
self.right = None
root = Node(1)
root.left = Node(2)
root.left.left = Node(4)
def count(node):
if node is None:
return 0
return 1 + count(node.left) + count(node.right)
def height(node):
if node is None:
return 0
return 1 + max(height(node.left), height(node.right))
print(count(root))
print(height(root))
It prints
3 3
Try it yourself
In pre-order, when is a node printed?
- After both its children
- Before either of its children
- Between its children
- Only if it is a leaf
What does this print?
Python
class Node:
def __init__(self, v):
self.value = v
self.left = None
self.right = None
r = Node("a")
r.left = Node("b")
r.right = Node("c")
def post(node):
if node is None:
return
post(node.left)
post(node.right)
print(node.value)
post(r)
Answer them in the app
🔍 Trees That Sort Themselves
One rule changes everything
A binary search tree keeps one promise: everything on the left is smaller than the node, everything on the right is bigger. Nothing else is new — but that rule makes finding things fast.
Python
class Node:
def __init__(self, v):
self.value = v
self.left = None
self.right = None
root = Node(8)
root.left = Node(3)
root.right = Node(10)
print(root.left.value < root.value)
print(root.right.value > root.value)
It prints
True True
Adding follows the rule
To insert, start at the root and go left when smaller, right when bigger, until you find an empty spot. The value lands exactly where the rule says it must.
Python
class Node:
def __init__(self, v):
self.value = v
self.left = None
self.right = None
def insert(node, value):
if node is None:
return Node(value)
if value < node.value:
node.left = insert(node.left, value)
else:
node.right = insert(node.right, value)
return node
root = None
for n in [8, 3, 10]:
root = insert(root, n)
print(root.value, root.left.value, root.right.value)
It prints
8 3 10
In-order always comes out sorted
This falls straight out of the rule: everything smaller is on the left, so visiting left-then-node-then-right visits the values in order. A BST sorts as you build it.
Python
class Node:
def __init__(self, v):
self.value = v
self.left = None
self.right = None
def insert(node, value):
if node is None:
return Node(value)
if value < node.value:
node.left = insert(node.left, value)
else:
node.right = insert(node.right, value)
return node
def in_order(node):
if node is None:
return
in_order(node.left)
print(node.value)
in_order(node.right)
root = None
for n in [5, 2, 8, 1]:
root = insert(root, n)
in_order(root)
It prints
1 2 5 8
Try it yourself
Where does 6 go in a BST whose root is 5?
- Left
- Right
- It replaces the root
- It cannot be added
Answer it in the app
⚡ Searching Fast
Half the tree disappears at every step
Looking for a value, you compare once and then ignore an entire side of the tree. That is why a BST search is quick even when the tree is huge.
Python
class Node:
def __init__(self, v):
self.value = v
self.left = None
self.right = None
root = Node(5)
root.left = Node(2)
root.right = Node(8)
def find(node, wanted):
if node is None:
return False
if node.value == wanted:
return True
if wanted < node.value:
return find(node.left, wanted)
return find(node.right, wanted)
print(find(root, 8))
print(find(root, 7))
It prints
True False
Count the steps yourself
Here the tree holds 7 values, and finding one takes 3 comparisons at most — because each step throws away half of what is left.
Python
class Node:
def __init__(self, v):
self.value = v
self.left = None
self.right = None
def insert(node, value):
if node is None:
return Node(value)
if value < node.value:
node.left = insert(node.left, value)
else:
node.right = insert(node.right, value)
return node
root = None
for n in [4, 2, 6, 1, 3, 5, 7]:
root = insert(root, n)
def steps(node, wanted, count=1):
if node.value == wanted:
return count
if wanted < node.value:
return steps(node.left, wanted, count + 1)
return steps(node.right, wanted, count + 1)
print(steps(root, 7))
print(steps(root, 4))
It prints
3 1
Finding the smallest and biggest
The smallest value is as far left as you can go, and the biggest as far right. No searching needed at all.
Python
class Node:
def __init__(self, v):
self.value = v
self.left = None
self.right = None
def insert(node, value):
if node is None:
return Node(value)
if value < node.value:
node.left = insert(node.left, value)
else:
node.right = insert(node.right, value)
return node
root = None
for n in [5, 2, 8, 1, 9]:
root = insert(root, n)
node = root
while node.left:
node = node.left
print(node.value)
node = root
while node.right:
node = node.right
print(node.value)
It prints
1 9
Try it yourself
A balanced BST holds 1000 values. Roughly how many steps to find one?
- About 1000
- About 500
- About 10
- Exactly 1
Answer it in the app
🏆 Tree Master
Try it yourself
What does this print?
Python
class Node:
def __init__(self, v):
self.value = v
self.left = None
self.right = None
r = Node(2)
r.left = Node(1)
r.right = Node(3)
def pre(node):
if node is None:
return
print(node.value)
pre(node.left)
pre(node.right)
pre(r)
Answer it in the app