Morris Traversal Inorder Without Stack in DSA Javascript - Time & Space Complexity
We want to understand how long Morris Traversal takes to visit all nodes in a binary tree without using extra memory for a stack.
How does the number of steps grow as the tree gets bigger?
Analyze the time complexity of the Morris Inorder Traversal code below.
function morrisInorder(root) {
let current = root;
while (current !== null) {
if (current.left === null) {
console.log(current.val);
current = current.right;
} else {
let predecessor = current.left;
while (predecessor.right !== null && predecessor.right !== current) {
predecessor = predecessor.right;
}
if (predecessor.right === null) {
predecessor.right = current;
current = current.left;
} else {
predecessor.right = null;
console.log(current.val);
current = current.right;
}
}
}
}
This code visits all nodes in a binary tree in order without using a stack or recursion by temporarily modifying the tree.
Look at the loops and repeated steps:
- Primary operation: Moving through nodes and finding the rightmost child (predecessor) in the left subtree.
- How many times: Each edge in the tree is visited at most twice: once to create a temporary link and once to remove it.
As the number of nodes (n) grows, the traversal visits each node and its edges a limited number of times.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 20 steps (each edge twice) |
| 100 | About 200 steps |
| 1000 | About 2000 steps |
Pattern observation: The steps grow roughly twice the number of edges, so it grows linearly with the number of nodes.
Time Complexity: O(n)
This means the time to traverse grows directly in proportion to the number of nodes in the tree.
[X] Wrong: "Because of the nested while loop, the time complexity must be quadratic O(n²)."
[OK] Correct: Each edge is visited at most twice, so the total work is still proportional to the number of nodes, not squared.
Understanding Morris Traversal shows you can save memory by clever tree manipulation while keeping time efficient, a skill valued in real-world coding.
What if we used recursion instead of Morris Traversal? How would the time and space complexity change?
Practice
Solution
Step 1: Understand Morris Traversal space usage
Morris Traversal modifies tree links temporarily to avoid using stack or recursion, so it uses O(1) extra space.Step 2: Compare with traditional methods
Traditional inorder traversal uses stack or recursion which requires O(h) space, where h is tree height.Final Answer:
It uses no extra space like stack or recursion -> Option DQuick Check:
Morris Traversal = O(1) space [OK]
- Thinking Morris Traversal is faster in time complexity
- Assuming it only works on binary search trees
- Believing it uses extra data structures like queues
curr in Morris Traversal?Solution
Step 1: Identify inorder predecessor in Morris Traversal
The inorder predecessor is the rightmost node in the left subtree ofcurr.Step 2: Traverse left subtree then rightmost path
Start atcurr.left, move right repeatedly until right child is null or points back tocurr.Final Answer:
Move to the left child ofcurrand then keep moving right until right child is null orcurr-> Option AQuick Check:
Inorder predecessor = left subtree's rightmost node [OK]
- Moving right child first instead of left
- Stopping after one left move without going right
- Confusing predecessor with successor
2 / \ 1 3Assuming the traversal prints node values in order.
Solution
Step 1: Understand inorder traversal order
Inorder traversal visits left subtree, then current node, then right subtree.Step 2: Apply Morris traversal on given tree
Left child is 1, root is 2, right child is 3, so output is [1, 2, 3].Final Answer:
[1, 2, 3] -> Option CQuick Check:
Inorder = left, root, right = [1, 2, 3] [OK]
- Confusing preorder or postorder with inorder
- Mixing node visit order
- Assuming traversal prints in reverse
while (curr !== null) {
if (curr.left === null) {
console.log(curr.val);
curr = curr.right;
} else {
let pred = curr.left;
while (pred.right !== null && pred.right !== curr) {
pred = pred.right;
}
if (pred.right === null) {
pred.right = curr;
curr = curr.left;
} else {
pred.right = null;
console.log(curr.val);
curr = curr.right;
}
}
}
What is the error in this code?Solution
Step 1: Check when node values are printed
Values are printed only whencurr.left === null, but after restoring links, printing is missing.Step 2: Identify missing print after restoring links
After settingpred.right = null, the code movescurr = curr.rightwithout printingcurr.val.Final Answer:
It misses printing the current node after restoring links -> Option AQuick Check:
Print missing after restoring links = bug [OK]
- Not printing node after restoring predecessor link
- Confusing when to update curr pointer
- Incorrectly modifying tree structure
Solution
Step 1: Identify when nodes are visited in Morris Traversal
Nodes are visited either whencurr.left === nullor after restoring the threaded link.Step 2: Collect values at both visit points
To collect all nodes in inorder, pushcurr.valboth whencurr.left === nulland after restoring links (before moving right).Final Answer:
Pushcurr.valboth whencurr.left === nulland after restoring links -> Option BQuick Check:
Collect values at both visits = correct inorder [OK]
- Collecting values only once misses nodes
- Collecting before moving left causes wrong order
- Ignoring second visit after restoring links
Ready to prove it?
Pass the test and this pattern moves unknown
