DSA Post-order Traversal

Post-order traversal visits the left subtree, then the right subtree, and finally the root, making it the safe order for deleting a tree or evaluating an expression.

What Is Post-order Traversal?

Post-order traversal is a depth-first strategy that processes nodes in left, right, root order. At each node, you first recursively traverse its entire left subtree, then its entire right subtree, and only after both are finished do you visit the node itself. A node's children - and everything beneath them - are always fully processed before the node.

That children-before-parent guarantee makes post-order the right tool whenever a node's own processing depends on results from its subtrees. It's the safe way to delete or free a tree, since every child is removed before its parent; it's how expression trees get evaluated, since an operator needs both operand values before it can compute anything; and it's how you'd compute a subtree's height or size, since those depend on the children's height or size first.

Recursive Post-order Traversal

Example: Recursive Post-order

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


def postorder(node, result=None):
    if result is None:
        result = []
    if node is None:
        return result
    postorder(node.left, result)
    postorder(node.right, result)
    result.append(node.value)
    return result


# Tree: root 5; left child 3, right child 8.
# 3's children are 2 and 4; 8's children are 7 and 9.
root = Node(5,
            Node(3, Node(2), Node(4)),
            Node(8, Node(7), Node(9)))

print(postorder(root))
# [2, 4, 3, 7, 9, 8, 5]

Iterative Post-order Traversal (Using Two Stacks)

Post-order is the trickiest of the three to write iteratively, because the root is visited last, not first. A simple trick fixes this: use one stack to explore the tree in root, right, left order (the mirror image of pre-order), pushing every node visited onto a second stack instead of a result list. Once the first stack is empty, popping the second stack from top to bottom yields exactly the left, right, root order that post-order requires.

Example: Iterative Post-order (Two Stacks)

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


def postorder_iterative(root):
    if root is None:
        return []

    stack1 = [root]
    stack2 = []

    while stack1:
        node = stack1.pop()
        stack2.append(node)
        if node.left:
            stack1.append(node.left)
        if node.right:
            stack1.append(node.right)

    return [node.value for node in reversed(stack2)]


root = Node(5,
            Node(3, Node(2), Node(4)),
            Node(8, Node(7), Node(9)))

print(postorder_iterative(root))
# [2, 4, 3, 7, 9, 8, 5]

Example: Evaluating an Expression Tree

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


def evaluate(node):
    if node.left is None and node.right is None:
        return node.value                # leaf: a number
    left_val = evaluate(node.left)        # left subtree first
    right_val = evaluate(node.right)      # then right subtree
    operator = node.value                 # root (operator) last

    if operator == '+':
        return left_val + right_val
    if operator == '-':
        return left_val - right_val
    if operator == '*':
        return left_val * right_val
    if operator == '/':
        return left_val / right_val
    raise ValueError(f'Unknown operator: {operator}')


# Expression tree for (3 + 4) * (10 - 6)
tree = Node('*',
            Node('+', Node(3), Node(4)),
            Node('-', Node(10), Node(6)))

print(evaluate(tree))   # 28
  • Visit order is left subtree, then right subtree, then root
  • Every node's children are fully processed before the node itself
  • The safe order for deleting or freeing a tree, since children go first
  • The natural traversal for evaluating expression trees and postfix notation
  • Runs in O(n) time and O(h) extra space, where h is the tree's height
TaskWhy Post-order Fits
Deleting a treeChildren must be freed before their parent
Evaluating an expression treeAn operator needs both operand values before it can compute a result
Computing subtree height or sizeA node's height or size depends on its children's height or size first
Note: If a computation for a node needs results from its children first - height, size, or evaluating an expression - post-order is almost always the traversal to reach for.

Exercise: DSA Tree Traversal

In in-order traversal of a binary tree, what is the visiting sequence?