Tree Traversals

In this lesson, we will think about how to traverse over a tree in a pre-defined, standard way.

All of the previous data structures that we have considered were linear, and iterating over a linear data structure has an obvious beginning and end. However, with a tree, the order in which we visit the nodes of the tree in an algorithm is not obvious. For example, consider the following tree:

A binary tree. The root is a 1, whose left and right children are 2 and 3. The 2 node's left and right children are 4 and 5. The 5 node's left and right children are 7 and 8. The 3 node has only a right child, which is a 6. The 6 node has only a left child, which is a 9.

One way of traversing this tree would be in level-order: the nodes are visited one level at a time, from top to bottom. A level-order traversal would therefore visit the nodes in the order 1 2 3 4 5 6 7 8 9.

However, we've already written an algorithm that does not visit the nodes in level-order! The num_nodes() method from the previous lesson, for example, accounted for the root node in the count, then recursively visited the left subtree, and then recursively visited the right subtree:

def __num_nodes(self, node):
    if node is None:
        return 0

    return 1 + self.__num_nodes(node.left) + self.__num_nodes(node.right)

For the above tree, __num_nodes() would have visited the nodes in the order 1 2 4 5 7 8 3 6 9.

Often, it does not matter the order in which the nodes are visited. For some scenarios, such as evaluating an algebraic expression that is modeled as a tree, the order does matter. Therefore, there are some well-known traversal orders, described below.

Pre-order traversal

To summarize, a pre-order traversal of a binary tree first visits the current node, then visits the current node's left subtree, and then recursively visits the current node's right subtree:

def __preorder_print(self, cur):
    if cur is None:
        return

    print(cur.val)
    self.__preorder_print(cur.left)
    self.__preorder_print(cur.right)

def preorder_print(self):
    self.__preorder_print(self.root)

In-order traversal

An in-order traversal of a binary tree first visits the left subtree recursively, then visits the current node, and then visits the right subtree recursively:

def __inorder_print(self, cur):
    if cur is None:
        return

    self.__inorder_print(cur.left)
    print(cur.val)
    self.__inorder_print(cur.right)

def inorder_print(self):
    self.__inorder_print(self.root)

Post-order traversal

An post-order traversal of a binary tree first visits the left subtree recursively, then visits the right subtree recursively, and then visits the current node:

def __postorder_print(self, cur):
    if cur is None:
        return

    self.__postorder_print(cur.left)
    self.__postorder_print(cur.right)
    print(cur.val)

def postorder_print(self):
    self.__postorder_print(self.root)