0
0
DSA Goprogramming

Morris Traversal Inorder Without Stack in DSA Go

Choose your learning style9 modes available
Mental Model
We visit nodes in order without using extra memory by temporarily linking nodes to their inorder predecessor.
Analogy: Imagine walking through a garden where you tie a ribbon from each tree back to the previous one you visited so you can return without getting lost or carrying a map.
    4
   / \
  2   5
 / \
1   3

No extra stack or recursion, just temporary links ->
Dry Run Walkthrough
Input: Binary tree: 4 -> 2 -> 1, 3 and 4 -> 5; perform inorder traversal using Morris method
Goal: Print nodes in inorder: 1 -> 2 -> 3 -> 4 -> 5 without stack or recursion
Step 1: Start at root 4, find predecessor in left subtree (rightmost of 2's subtree)
4 [curr] -> 2 -> 1 -> 3 -> 5
Temporary link not set yet
Why: We need to find where to return after left subtree is done
Step 2: Predecessor is 3, set 3's right pointer to 4 (threading)
3 -> 4 (thread)
4 [curr]
Left subtree traversal begins
Why: Thread allows returning to 4 after finishing left subtree
Step 3: Move curr to left child 2
2 [curr] -> 1 -> 3 -> 4 -> 5
Why: Traverse left subtree first
Step 4: Find predecessor of 2 (rightmost in left subtree), which is 1
1 -> 2 [curr] -> 3 -> 4 -> 5
Why: Prepare threading for node 2
Step 5: Set 1's right pointer to 2 (threading)
1 -> 2 (thread)
2 [curr]
Why: Thread allows returning to 2 after left subtree
Step 6: Move curr to left child 1
1 [curr] -> 2 -> 3 -> 4 -> 5
Why: Go deeper to leftmost node
Step 7: 1 has no left child, print 1 and move to right (which is threaded to 2)
Print 1
Curr moves to 2 via thread
Why: Visit node and return to parent
Step 8: At 2, thread detected from 1, remove thread, print 2, move to right child 3
Print 2
Thread removed
Curr -> 3
Why: Finish left subtree and move right
Step 9: 3 has no left child, print 3, move to right (threaded to 4)
Print 3
Curr -> 4 via thread
Why: Visit node and return to root
Step 10: At 4, thread detected from 3, remove thread, print 4, move to right child 5
Print 4
Thread removed
Curr -> 5
Why: Finish left subtree and move right
Step 11: 5 has no left child, print 5, move to right null
Print 5
Curr -> null
Why: Visit last node and end traversal
Result:
1 -> 2 -> 3 -> 4 -> 5 -> null
Annotated Code
DSA Go
package main

import "fmt"

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

func morrisInorder(root *TreeNode) {
	curr := root
	for curr != nil {
		if curr.Left == nil {
			fmt.Printf("%d -> ", curr.Val) // print current node
			curr = curr.Right // move right
		} else {
			// find inorder predecessor
			pre := curr.Left
			for pre.Right != nil && pre.Right != curr {
				pre = pre.Right // move to rightmost node in left subtree
			}
			if pre.Right == nil {
				pre.Right = curr // create thread
				curr = curr.Left // move left
			} else {
				pre.Right = nil // remove thread
				fmt.Printf("%d -> ", curr.Val) // print current node
				curr = curr.Right // move right
			}
		}
	}
	fmt.Print("null\n")
}

func main() {
	// Construct tree:
	//     4
	//    / \
	//   2   5
	//  / \
	// 1   3
	root := &TreeNode{4, nil, nil}
	root.Left = &TreeNode{2, nil, nil}
	root.Right = &TreeNode{5, nil, nil}
	root.Left.Left = &TreeNode{1, nil, nil}
	root.Left.Right = &TreeNode{3, nil, nil}

	morrisInorder(root)
}
for curr != nil {
loop until all nodes are visited
if curr.Left == nil {
if no left child, visit node and move right
fmt.Printf("%d -> ", curr.Val)
print current node value
curr = curr.Right
move to right child
pre := curr.Left
find inorder predecessor in left subtree
for pre.Right != nil && pre.Right != curr { pre = pre.Right }
move to rightmost node of left subtree or thread back to curr
if pre.Right == nil { pre.Right = curr; curr = curr.Left }
create thread and move left
else { pre.Right = nil; print curr; curr = curr.Right }
remove thread, visit node, move right
OutputSuccess
1 -> 2 -> 3 -> 4 -> 5 -> null
Complexity Analysis
Time: O(n) because each edge is visited at most twice during threading and removal
Space: O(1) because no extra stack or recursion is used, only pointers are modified temporarily
vs Alternative: Compared to recursive or stack-based inorder traversal which uses O(h) space, Morris traversal uses constant space but modifies tree temporarily
Edge Cases
Empty tree (root is nil)
No output, traversal ends immediately
DSA Go
for curr != nil {
Tree with single node
Prints the single node value and ends
DSA Go
if curr.Left == nil {
Tree with no left children (right skewed)
Traversal prints nodes in order by moving right only
DSA Go
if curr.Left == nil {
When to Use This Pattern
When asked to do inorder traversal without recursion or stack, think of Morris traversal because it uses threading to achieve O(1) space.
Common Mistakes
Mistake: Not removing the temporary thread after visiting the predecessor
Fix: Always remove the thread by setting predecessor's right pointer back to nil after visiting current node
Mistake: Moving curr to left child without creating thread
Fix: Create thread from predecessor to current before moving left to ensure return path
Summary
Performs inorder traversal without extra space by temporarily linking nodes to their inorder predecessors.
Use when you want inorder traversal with O(1) space and can modify the tree temporarily.
The key insight is creating and removing temporary threads to remember return paths without stack or recursion.