Python Arena
Step 3 of 4

A sorted binary tree

In the trees so far, a value could be anywhere, so a search had to visit every node. A sorted binary tree (also called a binary search tree) keeps its values in order. Everything in a node's left subtree is smaller than the node's value, and everything in its right subtree is greater:

         50
       /    \
     30      70
    /  \    /  \
  20   40  60   80

Searching

The order makes searching fast, in the same way as binary search. Compare the value you're looking for with the node's value. If it's smaller, it can only be in the left subtree, and if it's greater, only in the right one. So the search goes down one path and never needs the other side:

class Node:
    def __init__(self, value, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right


def find(node, value):
    if node is None:
        return False
    if value == node.value:
        return True
    if value < node.value:
        return find(node.left, value)
    return find(node.right, value)


tree = Node(50, Node(30, Node(20), Node(40)), Node(70, Node(60), Node(80)))
print(find(tree, 60))
print(find(tree, 65))
Output
True
False

The search for 65 goes from 50 to the right, from 70 to the left, and from 60 to the right, where there's nothing: None gives False. It looked at three nodes out of seven. In a tree with a million values whose branches are about equally deep, it would look at about twenty.

Adding values

To add a value, go down the same path as a search. When the way on is None, that's the place for the new node:

class Node:
    def __init__(self, value, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right


def add(node, value):
    if value < node.value:
        if node.left is None:
            node.left = Node(value)
        else:
            add(node.left, value)
    elif value > node.value:
        if node.right is None:
            node.right = Node(value)
        else:
            add(node.right, value)


def print_in_order(node):
    if node is not None:
        print_in_order(node.left)
        print(node.value)
        print_in_order(node.right)


tree = Node(42)
for value in [17, 99, 8, 23, 61, 23]:
    add(tree, value)
print_in_order(tree)
Output
8
17
23
42
61
99

A value that's already in the tree is neither smaller nor greater than the node that holds it, so neither branch adds it again: the second 23 is left out.

print_in_order visits the left subtree first, then the node, and then the right subtree. In a sorted tree, everything on the left is smaller and everything on the right is greater, so the values come out in increasing order.

The shape of the tree depends on the order the values are added in. Adding values that are already sorted, such as 1, 2, 3, 4, makes every new node a right child, and the "tree" becomes one long line. Searching it is then no faster than going through a list.

Your task

Challenge 1 of 3

Fill in the blanks so the search goes into the correct subtree. The program should print:

True
False
True

Challenge 2

Write a recursive function smallest(node) that returns the smallest value in a sorted binary tree. The tree is never empty. Use the order of the tree: you don't need to look at every node.

For example, this program:

tree = Node(50, Node(30, Node(20), Node(40)), Node(70))
print(smallest(tree))

prints:

20

Challenge 3

Write a recursive function in_order(node) that returns a list of all the values in a sorted binary tree, in increasing order. An empty tree, None, gives an empty list.

For example, this program:

tree = Node(50, Node(30, Node(20), Node(40)), Node(70, Node(60), Node(80)))
print(in_order(tree))

prints:

[20, 30, 40, 50, 60, 70, 80]