Morris Inorder Traversal
Inorder traversal visits the nodes of a binary tree in the following order:
Left Subtree → Root → Right Subtree
The usual recursive method uses the call stack to remember how to return to a node after processing its left subtree. An iterative method replaces the recursion stack with an explicit stack.
Both methods require additional space proportional to the height of the tree:
O(H)
Morris Inorder Traversal performs the same traversal without recursion and without an explicit stack. It temporarily creates links inside the tree so that traversal can return from a node’s left subtree to the node itself.
After using a temporary link, Morris traversal removes it. Therefore, the original tree structure is restored before the traversal finishes.
The major advantage is:
Auxiliary Space Complexity: O(1)
Given the root of a binary tree, return its inorder traversal using Morris traversal.
The traversal must:
Visit every node in inorder.
Avoid recursion.
Avoid using an explicit stack.
Use only constant auxiliary space.
Restore every temporary pointer before finishing.
Example
Consider the binary tree with the following relationships:
Node
1is the root.Node
2is the left child of1.Node
3is the right child of1.Node
4is the right child of2.
The inorder traversal is:
2 → 4 → 1 → 3
Therefore:
Answer = [2, 4, 1, 3]
Inorder Traversal.png
Why Do Normal Traversals Need Extra Space?
In recursive inorder traversal, the algorithm first moves toward the leftmost node.
Before moving left, the recursive call stack remembers:
The current node
Where traversal must return
Which right subtree remains unvisited
For example, while processing node 1, recursion moves into its left subtree. After completing that subtree, the stack returns traversal to node 1.
An iterative traversal stores the same pending nodes in an explicit stack.
Morris traversal asks an important question:
Can the tree itself temporarily store the information required to return to a node?
The answer is yes. A currently unused NULL right pointer can temporarily point back to the node that must be visited later.
Core Idea of Morris Traversal
Suppose the current node has a left subtree.
In inorder traversal, the node visited immediately before the current node is the rightmost node in its left subtree.
This node is called the current node’s inorder predecessor.
For a current node C:
Inorder predecessor = Rightmost node in C's left subtree
The predecessor normally has no right child. Morris traversal temporarily changes this NULL pointer so that it points to the current node:
predecessor.right = current
This temporary connection is called a thread.
After the left subtree is completed, traversal reaches the predecessor again. Its temporary right pointer leads back to the current node.
The thread is then removed:
predecessor.right = NULL
Only after removing the thread is the current node visited.
What Is an Inorder Predecessor?
The inorder predecessor of a node is the node that appears immediately before it in inorder traversal.
When a node has a left subtree, its predecessor is the rightmost node in that left subtree.
For node 1 in the example tree:
Move to its left child
2.Continue right from
2.Reach node
4.Node
4is the rightmost node in the left subtree of1.
Therefore:
Inorder predecessor of 1 = 4
Morris traversal temporarily creates:
4.right = 1
This thread provides a path back to node 1 after nodes 2 and 4 have been processed.
Morris Thread Lifecycle.png
Two Main Cases
During Morris traversal, a pointer named current represents the node currently being processed.
There are two main cases.
Current Node Has No Left Child
If current.left is NULL, there is no left subtree to process.
According to inorder order, the current node can be visited immediately.
After visiting it:
current = current.right
The right pointer may represent:
A normal right child, or
A temporary thread leading back to an ancestor
Morris Inorder Traversal Current Node has no Left Child.png
Current Node Has a Left Child
If a left child exists, find the rightmost node in the left subtree.
Let this node be predecessor.
Two situations are possible.
The Predecessor’s Right Pointer Is NULL
This is the first time the current node is encountered.
The left subtree has not yet been processed.
Create a temporary thread:
predecessor.right = current
Then move into the left subtree:
current = current.left
The current node is not visited yet because inorder requires its left subtree to be completed first.
The Predecessor’s Right Pointer Points to Current
This is the second time the current node is encountered.
The existing thread proves that the complete left subtree has already been processed.
Remove the thread:
predecessor.right = NULL
Now visit the current node and move to its right subtree:
current = current.right
Morris Inorder Traversal Current Node has a Left Child.png
Mental Model
Every node with a left subtree is encountered twice.
First Encounter
Find its inorder predecessor.
Create a thread from the predecessor to the current node.
Move into the left subtree.
Do not visit the current node yet.
Second Encounter
Find the same predecessor again.
Detect the existing thread.
Remove the thread.
Visit the current node.
Move into the right subtree.
A node without a left child is encountered once and visited immediately.
This gives a simple rule:
No left child: Visit and move right.
Left child with no thread: Create the thread and move left.
Left child with an existing thread: Remove the thread, visit, and move right.
Algorithm
Initialize
currentwith the root because traversal begins from the complete tree.If
currenthas no left child, visit it and move to its right pointer because no left subtree is pending.Otherwise, find the rightmost node in the left subtree, stopping when its right pointer is either
NULLor points tocurrent.If the predecessor’s right pointer is
NULL, create a temporary thread tocurrentand move to the left child.If the predecessor already points to
current, remove the thread, visitcurrent, and move to its right child.Repeat until
currentbecomesNULL, which means every node has been processed and every created thread has been removed.
Complete Dry Run
Morris Inorder Traversal.png
Why Does Morris Traversal Work?
Morris traversal preserves the required inorder sequence through two observations.
A Node Without a Left Subtree Can Be Visited Immediately
If no left subtree exists, there is nothing that must be processed before the current node.
Therefore, visiting the node and moving right follows inorder order.
A Node With a Left Subtree Must Be Visited After That Subtree
The inorder predecessor is the final node visited in the current node’s left subtree.
Creating a thread from the predecessor to the current node ensures that traversal returns immediately after completing the left subtree.
When the thread is encountered:
The left subtree is complete.
The thread is removed.
The current node is visited.
Traversal continues into the right subtree.
Therefore, every node is visited only after its complete left subtree and before its right subtree.
Why Does the Traversal Not Enter an Infinite Loop?
A temporary thread points from the predecessor back to the current node.
Without a special check, predecessor searching could repeatedly follow this thread and create a cycle.
Therefore, the predecessor search must stop when its right pointer is:
NULL, meaning no thread exists, orEqual to
current, meaning the previously created thread has been found
The second condition allows the traversal to detect the return from the left subtree, remove the thread, and continue forward.
Why Is the Time Complexity O(N)?
Morris traversal contains a search for the predecessor inside its main loop. This may initially appear to produce O(N²) time.
However, each relevant edge is followed only a constant number of times:
Once while moving down or right during traversal
Once while finding a predecessor to create a thread
Once again while finding the same predecessor to remove the thread
No edge is searched an unlimited number of times.
Therefore, the total number of pointer movements remains proportional to the number of nodes.
Time Complexity: O(N)
Why Is the Auxiliary Space O(1)?
Morris traversal does not use:
A recursion stack
An explicit stack
A queue
A parent mapping
A visited set
It maintains only a constant number of pointers, mainly:
currentpredecessor
Therefore:
Auxiliary Space Complexity: O(1)
The list containing the traversal result requires O(N) output space, but output space is generally excluded from auxiliary-space analysis.
Does Morris Traversal Modify the Tree?
Yes, but only temporarily.
Whenever the current node has an unprocessed left subtree, Morris traversal changes the predecessor’s NULL right pointer into a thread.
After the left subtree has been processed, the same thread is detected and removed.
The expected state transition is:
NULL → Temporary Thread → NULL
If the algorithm completes correctly, every changed pointer returns to its original value.
Therefore, the tree’s final structure remains unchanged.
Important Limitation of Temporary Modification
Although Morris traversal restores the tree, it modifies the structure while traversal is running.
This can be unsafe when:
The tree is immutable.
Multiple threads are reading or modifying the same tree.
Another operation traverses the tree simultaneously.
Traversal may stop unexpectedly before cleanup.
An exception or early return occurs after creating a thread.
Tree nodes are shared by another data structure.
In such situations, recursive or stack-based traversal may be safer despite using additional memory.
Comparison with Other Inorder Methods
Method | Time Complexity | Auxiliary Space | Modifies Tree Temporarily |
|---|---|---|---|
Recursive inorder |
|
| No |
Iterative inorder with stack |
|
| No |
Morris inorder |
|
| Yes |
For a balanced tree:
H = O(log N)
For a completely skewed tree:
H = O(N)
Morris traversal maintains constant auxiliary space in both cases.
Important Edge Cases
Empty Tree
If the root is NULL, current is also NULL.
The traversal loop never begins, so the result is empty.
Answer = []
Single Node
The root has no left child, so it is visited immediately.
Answer = [root]
Completely Right-Skewed Tree
Every node has no left child.
Each node is visited immediately, and no temporary threads are created.
Completely Left-Skewed Tree
Every non-leaf node requires a temporary thread.
Traversal first reaches the deepest left node and then follows the threads upward while removing them one by one.
Duplicate Values
Morris traversal depends on node pointers and tree structure, not comparisons between node values.
Duplicate values do not affect the traversal logic.
Existing Modified Pointers
Morris traversal assumes the input is a valid tree. If the structure already contains cycles or unexpected threads, predecessor searching may behave incorrectly.
Applications
Morris traversal is useful when:
Constant auxiliary space is required.
Recursion is not allowed.
The tree may be very deep.
Stack overflow must be avoided.
Traversal is required in a memory-constrained environment.
An interview asks for inorder traversal without stack or recursion.
The same threading concept is extended to Morris preorder or postorder traversal.
Common Mistakes
Visiting the current node before processing its left subtree.
Selecting the left child directly as the predecessor without moving to the rightmost node.
Searching for the predecessor without stopping when its right pointer equals
current.Creating a thread but forgetting to move
currentto its left child.Visiting the current node when creating the thread.
Forgetting to remove the temporary thread during the second encounter.
Moving right before visiting the current node after thread removal.
Treating a temporary thread as a normal right child.
Assuming the nested predecessor search makes the total complexity
O(N²).Counting the result list as constant auxiliary space.
Using Morris traversal on an immutable tree.
Returning early and leaving temporary threads inside the tree.
Assuming duplicate values prevent Morris traversal from working.
Forgetting to verify that the original tree is restored after traversal.
FAQs
Q1. Why is the current node not visited when a thread is created?
Creating a thread occurs during the first encounter with a node. Its left subtree has not been processed yet, so visiting it would violate the inorder order. The node is visited during the second encounter after the thread is found and removed.
Q2. Why is the rightmost node of the left subtree used?
The rightmost node is the final node visited in the left subtree during inorder traversal. Connecting it to the current node creates the correct return path after the entire left subtree is complete.
Q3. How can Morris traversal be O(N) when it repeatedly searches for predecessors?
Each tree edge is followed only a constant number of times while creating and removing threads. Across the complete traversal, these searches total O(N) pointer movements.
Q4. Does Morris traversal permanently change the tree?
No, if the traversal completes correctly. Every temporary thread is removed during the second encounter with its associated node, restoring the original structure.
Q5. What happens if Morris traversal stops before all threads are removed?
The tree may remain temporarily modified and can contain unexpected cycles. Implementations that may throw exceptions or return early must ensure that all created threads are cleaned up.
Q6. Can the Morris technique be used for other traversals?
Yes. Morris preorder uses a similar threading idea but visits nodes at a different time. Morris postorder is also possible, although it requires more complex edge-reversal operations.
Be the first to add a comment.