How to Implement Binary Tree Traversal?

How to Implement Binary Tree Traversal?

Binary trees are fundamental data structures in computer science, and traversing them efficiently is a crucial skill for any programmer. In this blog post, we’ll explore the three main traversal techniques—preorder, inorder, and postorder—and learn how to implement them in Python. We’ll also cover how to reconstruct a binary tree from traversal sequences and visualize the results.

Understanding Binary Tree Traversals

A binary tree consists of nodes, where each node can have at most two children: a left child and a right child. Traversal refers to the process of visiting each node exactly once in a specific order. The three primary traversal methods are defined by the order in which we visit the root node relative to its children:

  • Preorder Traversal: Visit the root node first, then recursively traverse the left subtree, followed by the right subtree. (Root → Left → Right)
  • Inorder Traversal: Recursively traverse the left subtree first, then visit the root node, followed by the right subtree. (Left → Root → Right)
  • Postorder Traversal: Recursively traverse the left subtree first, then the right subtree, and finally visit the root node. (Left → Right → Root)

Implementing Preorder Traversal

Let’s start with preorder traversal. We’ll create a function to build a binary tree from a preorder sequence (using ‘None’ to represent null nodes) and another function to visualize the tree.

import matplotlib.pyplot as plt​
​
class Node:​
    def __init__(self, value):​
        self.value = value​
        self.left = None​
        self.right = None​
​
def build_tree_from_preorder(preorder):​
    if not preorder:​
        return None​
    ​
    root_value = preorder.pop(0)​
    if root_value is None:​
        return None​
    ​
    root = Node(root_value)​
    root.left = build_tree_from_preorder(preorder)​
    root.right = build_tree_from_preorder(preorder)​
    return root​
​
def plot_tree(node, x, y, dx, dy, ax):​
    if node:​
        ax.text(x, y, str(node.value), style='italic', bbox={'facecolor': 'red', 'alpha': 0.5, 'pad': 10})​
        if node.left:​
            ax.plot([x, x - dx], [y, y - dy], 'k-')​
if __name__ == "__main__":​
    main_preorder()

Example 1: Right-skewed Tree

Input:

Enter preorder traversal (space-separated, use 'None' for null nodes): 1 None 2 None 3 None 4

Output:

Preorder Traversal: [1, None, 2, None, 3, None, 4]

The resulting tree structure:

  1​
   \​
    2​
     \​
      3​
       \​
        4

Example 2: Balanced Tree

Input:

Enter preorder traversal (space-separated, use 'None' for null nodes): 1 2 None None 3 4 None None 5 None None

Output:

Preorder Traversal: [1, 2, None, None, 3, 4, None, None, 5, None, None]

The resulting tree structure:

      1​
     / \​
    2   3​
       / \​
      4   5

Implementing Inorder Traversal

Inorder traversal is particularly useful for binary search trees (BSTs) because it visits nodes in ascending order. Here’s how to build a balanced binary tree from an inorder sequence:

import matplotlib.pyplot as plt​
​
class Node:​
    def __init__(self, value):​
        self.value = value​
        self.left = None​
        self.right = None​
​
def build_balanced_tree_from_inorder(inorder):​
    """Build a balanced binary tree from an inorder traversal array"""​
    if not inorder:​
        return None​
    ​
    mid = len(inorder) // 2​
    root = Node(inorder[mid])​
    ​
    root.left = build_balanced_tree_from_inorder(inorder[:mid])​
    root.right = build_balanced_tree_from_inorder(inorder[mid+1:])​
    ​
    return root​
​
def inorder_traversal(root):​
    """Inorder traversal (for verification)"""​
    if not root:​
        return []​
if __name__ == "__main__":​
    main_inorder()

Example Input:

Enter inorder traversal (space-separated): 4 2 5 1 6 3 7

Converting Between Traversal Sequences

One interesting problem is reconstructing a binary tree (and thus deriving the third traversal) from two given traversal sequences. Let’s explore some common conversions.

From Preorder and Postorder to Inorder

Note: A binary tree cannot be uniquely determined from preorder and postorder traversals alone. We’ll assume a full binary tree (each node has 0 or 2 children) for this implementation.

import matplotlib.pyplot as plt​
​
# Node class remains the same​
​
def build_tree_from_preorder_postorder(preorder, postorder):​
    if not preorder or not postorder:​
        return None​
    ​
    root_value = preorder[0]​
    root = Node(root_value)​
    ​
    if len(preorder) == 1:​
        return root​
    ​
    left_root_value = preorder[1]​
    left_root_idx = postorder.index(left_root_value)​
    ​
    root.left = build_tree_from_preorder_postorder(preorder[1:left_root_idx+2], postorder[:left_root_idx+1])​
    root.right = build_tree_from_preorder_postorder(preorder[left_root_idx+2:], postorder[left_root_idx+1:-1])​
    ​
    return root​
​
# inorder_traversal and plot_tree functions remain the same​
​
def main_pre_to_in():​
    preorder_input = input("Enter preorder traversal (space-separated): ")​
if __name__ == "__main__":​
    main_pre_to_in()

Input:

Enter preorder traversal (space-separated): 1 2 4 5 3 6 7Enter postorder traversal (space-separated): 4 5 2 6 7 3 1

Output:

Inorder traversal: [4, 2, 5, 1, 6, 3, 7]

From Inorder and Postorder to Preorder

import matplotlib.pyplot as plt​
​
# Node class remains the same​
​
def build_tree_from_inorder_postorder(inorder, postorder):​
    if not inorder or not postorder:​
        return None​
    ​
    root_value = postorder[-1]​
    root = Node(root_value)​
    ​
    mid_idx = inorder.index(root_value)​
    ​
    root.left = build_tree_from_inorder_postorder(inorder[:mid_idx], postorder[:mid_idx])​
    root.right = build_tree_from_inorder_postorder(inorder[mid_idx+1:], postorder[mid_idx:-1])​
    ​
    return root​
​
def preorder_traversal(root):​
    if not root:​
        return []​
    return [root.value] + preorder_traversal(root.left) + preorder_traversal(root.right)​
​
# plot_tree function remains the same​
​
if __name__ == "__main__":​
    main_in_post_to_pre()

From Inorder and Preorder to Postorder

import matplotlib.pyplot as plt​
​
# Node class remains the same​
​
def build_tree(preorder, inorder):​
    if not preorder or not inorder:​
        return None​
    ​
    root_value = preorder[0]​
    root = Node(root_value)​
    ​
    mid_idx = inorder.index(root_value)​
    ​
    root.left = build_tree(preorder[1:mid_idx+1], inorder[:mid_idx])​
    root.right = build_tree(preorder[mid_idx+1:], inorder[mid_idx+1:])​
    ​
    return root​
​
def postorder_traversal(root):​
    if not root:​
        return []​
    return postorder_traversal(root.left) + postorder_traversal(root.right) + [root.value]​
​
# plot_tree function remains the same​
​
def main_in_pre_to_post():​
if __name__ == "__main__":​
    main_in_pre_to_post()

Conclusion

Understanding binary tree traversals is essential for many algorithms and applications, from expression parsing to database indexing. The ability to convert between different traversal sequences demonstrates a deep understanding of tree structures.

In this post, we’ve covered:

  • The three main traversal techniques (preorder, inorder, postorder)
  • Implementing traversals in Python
  • Visualizing binary trees
  • Converting between traversal sequences

Practice with different tree structures to reinforce your understanding. You might also want to explore iterative implementations of these traversals, which use stacks instead of recursion.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *