Morris Traversal Inorder Without Stack in DSA C# - Time & Space Complexity
We want to understand how the Morris Traversal algorithm performs when visiting all nodes in a binary tree without using extra memory for a stack or recursion.
How does the number of steps grow as the tree gets bigger?
Analyze the time complexity of the following Morris Inorder Traversal code.
public void MorrisInorderTraversal(TreeNode root) {
TreeNode current = root;
while (current != null) {
if (current.left == null) {
Console.Write(current.val + " ");
current = current.right;
} else {
TreeNode 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.Write(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 tree links.
Look for 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) increases, the algorithm visits each node and its edges a limited number of times.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 20 visits (each edge twice) |
| 100 | About 200 visits |
| 1000 | About 2000 visits |
Pattern observation: The number of operations grows roughly twice the number of nodes, so it grows linearly.
Time Complexity: O(n)
This means the time to complete the traversal grows directly in proportion to the number of nodes in the tree.
[X] Wrong: "Since there is a nested loop to find the predecessor, the time complexity must be O(n²)."
[OK] Correct: Each edge is visited at most twice, so the total work is still proportional to n, not n squared.
Understanding Morris Traversal shows your skill in clever tree traversal without extra memory, a useful technique that impresses interviewers and deepens your grasp of tree algorithms.
"What if we used recursion instead of modifying tree links? How would the time and space complexity change?"
Practice
Solution
Step 1: Understand Morris Traversal space usage
Morris Traversal uses threaded binary trees to avoid recursion or stack, so it uses O(1) extra space.Step 2: Compare with other methods
Recursive and stack-based traversals use O(h) space where h is tree height, so Morris Traversal saves memory.Final Answer:
It uses no extra space for stack or recursion. -> Option CQuick Check:
Space complexity = O(1) [OK]
- Thinking it is faster than recursive traversal
- Assuming it only works on BSTs
- Believing it permanently changes the tree
Solution
Step 1: Identify starting point for traversal
Morris traversal starts from the root node to cover the entire tree.Step 2: Check initialization correctness
Initializing current as root ensures traversal begins at the tree's root.Final Answer:
TreeNode current = root; -> Option AQuick Check:
Start traversal at root node [OK]
- Starting from null causes no traversal
- Starting from left or right child misses nodes
2
/ \
1 3
Assuming the traversal prints node values separated by spaces.Solution
Step 1: Understand inorder traversal order
Inorder traversal visits left subtree, root, then right subtree.Step 2: Apply Morris traversal order on given tree
Left child 1, then root 2, then right child 3 results in "1 2 3".Final Answer:
1 2 3 -> Option BQuick Check:
Inorder = left-root-right = 1 2 3 [OK]
- Confusing preorder or postorder with inorder
- Misreading tree structure
while (current != null) {
if (current.left == null) {
Console.Write(current.val + " ");
current = current.right;
} else {
TreeNode 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.Write(current.val + " ");
current = current.right;
}
}
}
What is the bug in this code?Solution
Step 1: Analyze inner while loop condition
The loop should continue while predecessor.right != null && predecessor.right != current to reach the rightmost node in left subtree.Step 2: Check given condition correctness
The code uses predecessor.right == current, which fails to traverse the right chain properly, as it only continues if already threaded back.Step 3: Why other options are wrong
A: left null is handled in if. C: reset happens in else. D: print placement is correct for inorder.Final Answer:
The inner while loop should check predecessor.right != current instead of predecessor.right == current. -> Option DQuick Check:
Loop condition must be != null && != current [OK]
- Confusing loop condition signs
- Forgetting to reset threaded pointers
- Misplacing print statements
Solution
Step 1: Identify when nodes are visited in Morris traversal
Nodes are visited (printed) when current.left is null or after removing threaded link.Step 2: Counting nodes correctly
Incrementing counter when printing nodes ensures counting all nodes exactly once.Step 3: Why other options fail
Incrementing only on creating or removing threaded links misses nodes or counts duplicates.Final Answer:
Increment a counter each time you print a node's value during traversal. -> Option AQuick Check:
Count nodes when visiting them [OK]
- Counting on threaded link creation/removal
- Counting only left-null nodes
- Missing some nodes in count
Ready to prove it?
Pass the test and this pattern moves unknown
