On this page
Description
Morris traversal walks a binary tree in order using O(1) extra space: no recursion, no stack. Before going down a node's left subtree, it makes that subtree's rightmost node point back to the node. That temporary link is called a "thread", and it lets the walk find its way back. When the walk returns along the thread, it removes it. It's the answer when the interviewer says "now without the stack".
We'll go from simple to more technical:
- The pattern: thread, descend, come back, unthread.
- Inorder versus preorder: the same loop, a different moment to visit.
- Where borrowing an unused pointer saves real memory.
- The risks: threads left behind, and mutating a tree others can see.
The approach (inorder; preorder is a one-line change)
Recursion remembers "come back here after the left subtree" on the call stack: O(h) extra space, where h is the tree's height. Morris stores that reminder inside the tree instead. In inorder, the last node visited in a node's left subtree is that subtree's rightmost node, the node's predecessor. The predecessor has no right child, so its right pointer is free. Point it at the current node, go left, and when the walk later arrives through that pointer, you know the left side is done.
| At | What happens | Visited so far |
|---|---|---|
| 4 | its predecessor is 3: thread 3 to 4, go left | |
| 2 | its predecessor is 1: thread 1 to 2, go left | |
| 1 | no left child: visit, follow the thread to 2 | 1 |
| 2 | the thread is there, so the left side is done: remove it, visit, go right | 1 2 |
| 3 | no left child: visit, follow the thread to 4 | 1 2 3 |
| 4 | the thread is there: remove it, visit, go right | 1 2 3 4 |
| 6 | no left child: visit, go right to null and stop | 1 2 3 4 6 |
Java
TreeNode curr = root;
while (curr != null) {
if (curr.left == null) {
visit(curr); // no left subtree - visit now, move right
curr = curr.right;
} else {
TreeNode pred = curr.left;
while (pred.right != null && pred.right != curr) pred = pred.right; // find predecessor
if (pred.right == null) {
pred.right = curr; // create the thread
curr = curr.left; // descend, without visiting curr yet (inorder: node comes after left)
} else {
pred.right = null; // thread already exists - we've returned via it; remove it
visit(curr); // now visit curr (its left subtree is done)
curr = curr.right;
}
}
}
Python
curr = root
while curr is not None:
if curr.left is None:
visit(curr) # no left subtree - visit now, move right
curr = curr.right
else:
pred = curr.left
while pred.right is not None and pred.right is not curr:
pred = pred.right # find predecessor
if pred.right is None:
pred.right = curr # create the thread
curr = curr.left # descend, without visiting curr yet (inorder: node comes after left)
else:
pred.right = None # thread already exists - we've returned via it; remove it
visit(curr) # now visit curr (its left subtree is done)
curr = curr.right
For preorder, visit curr when you create the thread, before going left, instead of when you remove it. Nothing else changes.
Where you'll see it
O(1) space matters on small embedded devices. It also matters when many separate trees are walked at once, so stack space adds up. When "no stack, no recursion" is a hard rule rather than a preference, Morris is the standard answer.
Which tool for which job?
| You need | Use |
|---|---|
| Inorder or preorder, simplest | recursion |
| The same on a very deep tree | an explicit stack |
| The same with O(1) extra space | Morris traversal |
| A tree others may read meanwhile | not Morris - it mutates the tree |
| Early exit (k-th smallest) with Morris | finish the walk or undo the threads |
| Postorder in O(1) space | possible, but rarely worth it |
Common mistakes
| Mistake | What happens | Do this instead |
|---|---|---|
| Not removing the thread | the tree keeps phantom links | pred.right = null on the way back |
| Visiting at the wrong moment | preorder instead of inorder (or the reverse) | inorder visits when unthreading |
| Returning early mid-traversal | threads stay in the tree | finish, or clean up before returning |
Searching for the predecessor without the != cur check | an infinite loop around the thread | stop at null or at cur |
| Using it on shared or read-only trees | other readers see a broken tree | use a stack |
Check yourself
Try to answer before opening each one - recalling beats re-reading.
1. An implementation creates threads but never removes them. What state is the tree in afterwards?
Show answer
Broken. Every predecessor that got a thread still points back up at an ancestor instead of at null. Any later walk that follows right pointers takes those links and gives wrong results, with no error to warn you.
2. What single change turns Morris inorder into Morris preorder?
Show answer
When the node is visited. Preorder visits it when the thread is created, before going left. Inorder visits it when the thread is removed, after the left subtree is done. Every other line is the same.
3. Why does the predecessor search also stop at pred.right == curr, not just at null?
Show answer
On the way back, the predecessor's right is the thread to curr. A search that stopped only at null would follow the thread onto curr and keep going, threading again and looping forever. Stopping on the thread lets the next check tell the two visits apart: null means create the thread, anything else means remove it.
Key takeaways
- Morris borrows the predecessor's unused
rightpointer as a "come back here" link, so no stack is needed. - Remove every thread on the way back, or the tree stays broken.
- Inorder visits when removing the thread; preorder visits when creating it.
- The predecessor search stops at null or at
curr. - O(1) space costs a temporarily changed tree and trickier code. Use it when the space limit is real.