reverse postorder
graph traversal
depth-first search
algorithm
computer science

What is the reverse postorder?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Reverse postorder is the reversed sequence of nodes as they finish in a depth-first search (DFS). In postorder, a node is recorded after all its descendants are visited. Reversing this order puts parents before children, which is equivalent to topological order for directed acyclic graphs (DAGs). Reverse postorder is widely used in compiler optimizations (data flow analysis, SSA construction), topological sorting, and dependency resolution.

Postorder vs Reverse Postorder

 
1Graph:
2    523
3    50
4    40
5    41
6    231
7
8DFS from node 5, then 4:
9Postorder:         [3, 1, 2, 0, 5, 4]  (nodes in order of DFS completion)
10Reverse Postorder: [4, 5, 0, 2, 1, 3]  (reversed — parents before children)

In postorder, a node is added to the list when its DFS subtree is fully explored. Reversing this list gives an ordering where every node appears before the nodes it depends on.

Implementation in Python

python
1def reverse_postorder(graph):
2    """Return nodes in reverse postorder (topological order for DAGs)."""
3    visited = set()
4    postorder = []
5
6    def dfs(node):
7        visited.add(node)
8        for neighbor in graph.get(node, []):
9            if neighbor not in visited:
10                dfs(neighbor)
11        postorder.append(node)  # add AFTER visiting all descendants
12
13    # Visit all nodes (handles disconnected graphs)
14    for node in graph:
15        if node not in visited:
16            dfs(node)
17
18    # Reverse the postorder
19    return postorder[::-1]
20
21# Example graph (adjacency list)
22graph = {
23    5: [2, 0],
24    4: [0, 1],
25    2: [3],
26    3: [1],
27    0: [],
28    1: [],
29}
30
31result = reverse_postorder(graph)
32print(result)  # [4, 5, 0, 2, 3, 1] or another valid topological order

The key line is postorder.append(node) — it is called after all neighbors are recursively visited, ensuring descendants are added before their parents in the postorder list.

Iterative Implementation

python
1def reverse_postorder_iterative(graph):
2    """Iterative DFS to compute reverse postorder."""
3    visited = set()
4    postorder = []
5
6    for start in graph:
7        if start in visited:
8            continue
9        stack = [(start, False)]
10        while stack:
11            node, processed = stack.pop()
12            if processed:
13                postorder.append(node)
14                continue
15            if node in visited:
16                continue
17            visited.add(node)
18            # Push the node again with processed=True for postorder recording
19            stack.append((node, True))
20            # Push neighbors in reverse order for consistent traversal
21            for neighbor in reversed(graph.get(node, [])):
22                if neighbor not in visited:
23                    stack.append((neighbor, False))
24
25    return postorder[::-1]

The iterative version uses a stack with a (node, processed) tuple. When processed is True, the node is added to the postorder list (equivalent to the recursive DFS returning).

Binary Tree Example

python
1class TreeNode:
2    def __init__(self, val, left=None, right=None):
3        self.val = val
4        self.left = left
5        self.right = right
6
7def tree_postorder(root):
8    """Postorder: left, right, root."""
9    if root is None:
10        return []
11    return (tree_postorder(root.left) +
12            tree_postorder(root.right) +
13            [root.val])
14
15def tree_reverse_postorder(root):
16    """Reverse postorder: root, right, left (reversed)."""
17    return tree_postorder(root)[::-1]
18
19#       1
20#      / \
21#     2   3
22#    / \
23#   4   5
24
25root = TreeNode(1,
26    TreeNode(2, TreeNode(4), TreeNode(5)),
27    TreeNode(3)
28)
29
30print(tree_postorder(root))          # [4, 5, 2, 3, 1]
31print(tree_reverse_postorder(root))  # [1, 3, 2, 5, 4]

For a binary tree, postorder visits left-right-root. Reverse postorder visits root before children, similar to a modified preorder.

Topological Sort Connection

python
1# Reverse postorder IS topological order for DAGs
2
3# Dependency graph: task A must finish before task B
4dependencies = {
5    "compile":  ["link"],
6    "test":     [],
7    "link":     ["test"],
8    "package":  [],
9    "deploy":   ["package"],
10    "build":    ["compile", "test"],
11}
12
13order = reverse_postorder(dependencies)
14print(order)
15# ['build', 'compile', 'link', 'test', 'deploy', 'package']
16# or another valid topological ordering

For DAGs, reverse postorder produces a valid topological sort — every node appears before the nodes that depend on it. This is why Kahn's algorithm and DFS-based topological sort both work.

Use in Compiler Optimization

 
1// Compiler data flow analysis uses reverse postorder for:
2
3// 1. Reaching definitions: propagate variable definitions forward
4// 2. Live variable analysis: propagate variable uses backward
5// 3. Constant propagation: substitute known constants
6// 4. SSA construction: place phi functions at dominance frontiers
7
8// Processing basic blocks in reverse postorder ensures that
9// (for forward analyses) all predecessors of a block are
10// processed before the block itself, leading to faster convergence.
11
12// Control Flow Graph:
13//   Entry → B1 → B2 → B3 → Exit
14//               ↘ B4 ↗
15//
16// Reverse Postorder: [Entry, B1, B2, B4, B3, Exit]
17// Processing in this order means B1's results are ready when B2 is processed

Compilers process basic blocks in reverse postorder to ensure data flow equations converge in fewer iterations. For reducible control flow graphs, a single pass in reverse postorder computes correct results for forward data flow problems.

Reverse Postorder vs Preorder vs BFS

python
1graph = {0: [1, 2], 1: [3], 2: [3], 3: []}
2
3# Preorder (DFS): record when first visited
4# [0, 1, 3, 2]
5
6# Postorder (DFS): record when DFS backtracks
7# [3, 1, 2, 0]
8
9# Reverse Postorder: reversed postorder
10# [0, 2, 1, 3]
11
12# BFS Level Order:
13# [0, 1, 2, 3]
TraversalWhen Node is RecordedTopological Order?
PreorderWhen first visitedNo
PostorderWhen all descendants doneNo (reversed is yes)
Reverse PostorderReversed postorderYes (for DAGs)
BFSWhen dequeuedOnly with Kahn's algorithm

Common Pitfalls

  • Confusing reverse postorder with reversed preorder: They are different. Preorder records nodes when first visited; postorder records when DFS backtracks. Reversing preorder does not give a valid topological sort. Only reversing postorder does.
  • Applying reverse postorder to cyclic graphs for topological sort: Reverse postorder only gives a valid topological order for DAGs. For graphs with cycles, there is no valid topological order. The DFS will still complete, but the result may visit nodes in a back-edge before their dependents.
  • Forgetting to handle disconnected graphs: If the graph has multiple connected components, starting DFS from a single node misses the other components. Iterate over all nodes and start DFS from any unvisited node.
  • Infinite recursion on large graphs: Python's default recursion limit is 1000. For graphs with more than ~1000 nodes, use the iterative implementation or increase the limit with sys.setrecursionlimit().
  • Confusing "reverse postorder" with "reverse DFS": "Reverse DFS" sometimes means DFS on the transpose (reversed edges) graph. "Reverse postorder" means reversing the list of nodes produced by postorder DFS. These are different concepts used in different algorithms (e.g., Kosaraju's algorithm uses both).

Summary

  • Reverse postorder is the reversed list of nodes as they finish in DFS
  • For DAGs, reverse postorder equals topological order — every node comes before its dependents
  • Implementation: run DFS, append nodes on backtrack, then reverse the list
  • Compilers use reverse postorder to process basic blocks efficiently in data flow analysis
  • Reverse postorder is different from reversed preorder — only the former gives topological order
  • Use iterative DFS for large graphs to avoid Python's recursion limit

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.