In our embedded C++ runtime, each thread has a strictly limited stack frame (64KB). When traversing deeply skewed binary trees with millions of nodes, recursive DFS triggers a stack overflow, and allocating an explicit heap stack (std::vector) exceeds our device RAM limit.
I heard about Morris Traversal which claims to do a full Inorder/Preorder tree traversal in strict O(1) auxiliary space without a call stack. How does it work under the hood, and does it corrupt the tree structure?
Here is the full modern C++ implementation of Morris Inorder Traversal. Notice how it cleanly establishes and tears down temporary predecessor right-pointers, completely restoring the original tree topology before returning.
Complexity:
O(N)time and strictO(1)auxiliary space. No call stack or heap allocation.Morris Traversal is one of the most brilliant algorithms in computer science. It solves the exact constraint you’re facing: how do you traverse a tree without spending any extra memory on a stack?
1. The Core Secret: Threaded Binary Trees
When you are at a node and go deep into its left subtree, how do you get back up to the node without a parent pointer or call stack? Normally, you need a stack to remember the return path.
J. H. Morris realized something clever: in every binary tree, about half of all pointers are NULL! Every leaf node has a
nullright child that is sitting there doing nothing.Morris repurposes these unused
nullpointers as temporary bridge wires (called “threads”) back to the inorder successor:null, point it back to the current node:predecessor->right = current. Then movecurrent = current->left.current, that means you have already finished visiting the left subtree! You print/recordcurrent->val, restore the pointer tonull(repairing the tree), and movecurrent = current->right!When the algorithm finishes, the tree is 100% restored to its original state. Zero memory allocated, zero permanent mutations!
Clean C++20 Morris Inorder Traversal
Complexity & Trade-offs
O(N). Even though we search for predecessors, each edge in the tree is traversed at most 3 times (once to find predecessor, once to create thread, once to remove thread).3 * (N - 1) = O(N).O(1)auxiliary space. Just two pointers (currandpred). No call stack, no heap allocations.