DrawCode Algorithm Visualizer • Tree Traversal • Hard

Morris Traversal Algorithm

Tags: Tree, Binary Tree, Inorder, Traversal, Threading, Space Optimization, Interview

1. One-Liner

Morris Traversal performs an inorder traversal of a binary tree using O(1) extra space — no stack, no recursion — by temporarily creating "threads" (links) in the tree.


2. The Problem It Solves

Standard inorder traversal of a binary tree uses:

  • Recursion: O(h) space on the call stack, where h = height of tree. For a skewed tree, h = n.
  • Iterative with stack: O(h) explicit stack space.
  • In embedded systems, real-time systems, or memory-constrained environments, even O(h) extra space can be too much. Morris Traversal solves this by achieving O(1) auxiliary space while still visiting every node in inorder.


    3. The Core Idea

    Imagine you're walking through a forest of tree nodes. Normally, you'd drop breadcrumbs (push to stack) to find your way back. Morris's insight:

    Instead of breadcrumbs, create temporary "shortcuts" (threads) in the tree itself.

    For each node with a left child:

    1. Find the inorder predecessor (rightmost node in the left subtree).

    2. If the predecessor's right pointer is null, create a thread (set it to point back to the current node), then go left.

    3. If the predecessor already points back to us (thread exists), we've finished the left subtree. Remove the thread, visit the node, and go right.

    The tree is temporarily modified but fully restored by the end.


    4. How It Works (Step-by-Step)

    StepWhat Happens
    1. InitializeSet current = root, create empty result list
    2. Check currentWhile current != null, continue the loop
    3. No left child?Visit current, move to current.right
    4. Has left childFind inorder predecessor (rightmost in left subtree)
    5a. No thread yetCreate thread: predecessor.right = current. Move left.
    5b. Thread existsLeft subtree done. Remove thread. Visit current. Move right.
    6. RepeatContinue until current is null

    Key insight: Each edge in the tree is traversed at most 3 times (once going down, at most twice for predecessor finding). Total time is O(n), and we only use O(1) extra space (just a few pointers).


    5. Dry Run Example

    Tree:

            4
           / \
          2   6
         / \ / \
        1  3 5  7
    StepCurrentActionThreadResult So Far
    14Has left. Pred=3. No thread. Create 3→4. Go left.3→4[]
    22Has left. Pred=1. No thread. Create 1→2. Go left.1→2, 3→4[]
    31No left. Visit 1. Move right (thread → 2).1→2, 3→4[1]
    42Has left. Pred=1. Thread exists (1→2). Remove. Visit 2. Move right.3→4[1, 2]
    53No left. Visit 3. Move right (thread → 4).3→4[1, 2, 3]
    64Has left. Pred=3. Thread exists (3→4). Remove. Visit 4. Move right.[1, 2, 3, 4]
    76Has left. Pred=5. No thread. Create 5→6. Go left.5→6[1, 2, 3, 4]
    85No left. Visit 5. Move right (thread → 6).5→6[1, 2, 3, 4, 5]
    96Has left. Pred=5. Thread exists. Remove. Visit 6. Move right.[1, 2, 3, 4, 5, 6]
    107No left. Visit 7. Move right (null). Done![1, 2, 3, 4, 5, 6, 7]

    Final result: [1, 2, 3, 4, 5, 6, 7] — perfect inorder!


    6. Key Properties

    PropertyValue
    Extra spaceO(1) — only a few pointers
    Time complexityO(n) — each edge traversed ≤ 3 times
    Modifies tree?Temporarily yes, but fully restored
    Works forInorder, can be adapted for preorder
    Thread safetyNot thread-safe (modifies tree structure)
    Predecessor found inO(h) per node, but amortized O(n) total

    7. Where It Is Used

    Use CaseWhy Morris Traversal
    Embedded systemsCannot afford O(h) stack space
    Database B-treesConstant-space tree scanning
    Real-time systemsPredictable memory usage
    Competitive programmingAsked in hard interview problems
    BST validationInorder should be sorted — Morris + O(1) check
    Kth smallest in BSTMorris + counter to find kth element

    8. Interview Tips

    What interviewers want to hear:

    1. You understand why O(1) space matters (stack overflow risk, memory-constrained systems)

    2. You can explain the threading concept (temporary right pointers to create "breadcrumbs")

    3. You know the tree is fully restored after traversal (no permanent modification)

    4. You can trace through a dry run step by step

    5. You understand it's O(n) time despite the nested loops (amortized analysis)

    Common follow-up questions:

  • "Why is it O(n) time despite the inner while loop?" → Each edge is traversed at most 2 times for predecessor finding. Total work across all nodes is O(n).
  • "Can you do preorder with Morris?" → Yes! Visit the node when creating the thread (before going left) instead of when removing it.
  • "What's the downside?" → Temporarily modifies the tree. Not safe in concurrent/multi-threaded environments. Cannot be used on immutable trees.
  • "How do you find the inorder predecessor?" → Go to left child, then keep going right until you hit null or the current node.
  • "Can you do postorder with Morris?" → Possible but complex. Usually not asked in interviews.

  • 9. Comparison with Other Traversal Methods

    MethodSpaceTimeModifies Tree?Best For
    RecursiveO(h)O(n)NoSimple implementation
    Iterative (Stack)O(h)O(n)NoWhen recursion depth is a concern
    Morris TraversalO(1)O(n)TemporarilyMemory-constrained systems
    Parent PointerO(1)O(n)Needs extra fieldIf tree nodes have parent pointers

    10. Complexity

    MetricValue
    Time ComplexityO(n) — every node visited exactly once
    Space ComplexityO(1) auxiliary — only pointer variables
    Predecessor SearchO(n) total amortized across all nodes
    Tree Restoration100% — all temporary threads removed

    Implementation Example (PYTHON)

    class TreeNode:
        def __init__(self, val=0, left=None, right=None):
            self.val = val
            self.left = left
            self.right = right
    
    def morris_inorder(root):
        result = []
        current = root
    
        while current is not None:
            if current.left is None:
                result.append(current.val)
                current = current.right
            else:
                predecessor = current.left
                while (predecessor.right and
                       predecessor.right != current):
                    predecessor = predecessor.right
    
                if predecessor.right is None:
                    predecessor.right = current
                    current = current.left
                else:
                    predecessor.right = None
                    result.append(current.val)
                    current = current.right
    
        return result

    Interactive Visualizer Workspace

    Explore step-by-step interactive animations, memory state tracking, and live multi-language execution in DrawCode.

    Launch Interactive Visualizer