Given a binary tree, there are a couple ways to traverse the tree, each visiting nodes in a different order. All traversal algorithms have time and space complexity. (Or not???)

Pre-Order Traversal

Recursive Implementation

def preorder(root: Optional[None]):
	if root is None:
		return
    visit(root)
    preorder(root.left)
    preorder(root.right)

Iterative Implementation

def preorder(root: Optional[None]):
	st = [root]
	while st:
		node = st.pop()
		visit(node)
		
		if node is None:
			continue
		st.append(node.right)
		st.append(node.left)

Post-Order Traversal

Recursive Implementation

def postorder(root: Optional[None]):
	if root is None:
		return
    preorder(root.left)
    preorder(root.right)
    visit(root)

Iterative Implementation

def postorder(root: Optional[None]):
	st = []
	node = root
	lastNode = None
	
	while st or node:
		if node:
			st.append(node)
			node = node.left
		else:
			top = st[-1]
			if top.right and top.right != lastNode:
				node = top.right
			else:
				visit(top)	
				st.pop()
				lastNode = top

In-Order Traversal

Recursive Implementation

def inorder(root: Optional[None]):
	if root is None:
		return
    preorder(root.left)
    visit(root)
    preorder(root.right)
def inorder(root: Optional[None]):
	st = []
	node = root
	
	while st or node:
		if node:
			st.append(node)
			node = node.left
		else:
			node = st.pop()
			visit(node)
			node = node.right

Morris In-Order Traversal

Morris traversal is a traversal technique that results in in-order traversal but does not rely on any stack or recursion to run. Instead, it temporarily modifies the tree in-place. This way, it achieves space complexity while keeping the same time complexity as other traversal algorithms.

It rewrites the tree into its threaded form such that an edge between the rightmost node of a node’s left subtree to the the node itself. It then continues doing so recursively for the node’s left node.

graph TB
    subgraph row1 [" "]
        subgraph S8 ["Step 8: Remove thread 1→2, process node 3"]
            A7((4)) --> B7((2))
            A7 --> C7((6))
            B7 --> D7((1))
            B7 --> E7((3))
            C7 --> F7((5))
            C7 --> G7((7))
            style E7 fill:#4caf50,stroke:#333,stroke-width:3px
            style B7 fill:#e0e0e0,stroke:#333,stroke-width:1px
        end
        
        subgraph S6 ["Step 6: Process node 1, follow thread"]
            A5((4)) --> B5((2))
            A5 --> C5((6))
            B5 --> D5((1))
            D5 -.->|thread| B5
            B5 --> E5((3))
            E5 -.->|thread| A5
            C5 --> F5((5))
            C5 --> G5((7))
            style D5 fill:#4caf50,stroke:#333,stroke-width:3px
            style B5 fill:#ffeb3b,stroke:#333,stroke-width:3px
        end
		
        subgraph S4 ["Step 4: Find predecessor of 2 (node 1)"]
            A3((4)) --> B3((2))
            A3 --> C3((6))
            B3 --> D3((1))
            B3 --> E3((3))
            E3 -.->|thread| A3
            C3 --> F3((5))
            C3 --> G3((7))
            style D3 fill:#ff9800,stroke:#333,stroke-width:3px
            style B3 fill:#ffeb3b,stroke:#333,stroke-width:3px
        end
		
        subgraph S2 ["Step 2: Find predecessor of 4 (node 3)"]
            A1((4)) --> B1((2))
            A1 --> C1((6))
            B1 --> D1((1))
            B1 --> E1((3))
            C1 --> F1((5))
            C1 --> G1((7))
            style E1 fill:#ff9800,stroke:#333,stroke-width:3px
            style A1 fill:#ffeb3b,stroke:#333,stroke-width:3px
        end
    end
    
    subgraph row2 [" "]
        subgraph S7 ["Step 7: Remove thread 3→4, process node 2"]
            A6((4)) --> B6((2))
            A6 --> C6((6))
            B6 --> D6((1))
            D6 -.->|thread| B6
            B6 --> E6((3))
            C6 --> F6((5))
            C6 --> G6((7))
            style B6 fill:#4caf50,stroke:#333,stroke-width:3px
            style A6 fill:#e0e0e0,stroke:#333,stroke-width:1px
        end
        
        subgraph S5 ["Step 5: Create thread 1→2, move to left"]
            A4((4)) --> B4((2))
            A4 --> C4((6))
            B4 --> D4((1))
            D4 -.->|thread| B4
            B4 --> E4((3))
            E4 -.->|thread| A4
            C4 --> F4((5))
            C4 --> G4((7))
            style D4 fill:#ffeb3b,stroke:#333,stroke-width:3px
        end
	
        subgraph S3 ["Step 3: Create thread 3→4, move to left"]
            A2((4)) --> B2((2))
            A2 --> C2((6))
            B2 --> D2((1))
            B2 --> E2((3))
            E2 -.->|thread| A2
            C2 --> F2((5))
            C2 --> G2((7))
            style B2 fill:#ffeb3b,stroke:#333,stroke-width:3px
        end
		
        subgraph S1 ["Step 1: Initial State - Current = 4"]
            A((4)) --> B((2))
            A --> C((6))
            B --> D((1))
            B --> E((3))
            C --> F((5))
            C --> G((7))
            style A fill:#ffeb3b,stroke:#333,stroke-width:3px
        end
    end
	
    style row1 fill:none,stroke:none
    style row2 fill:none,stroke:none

Implementation

def morris_inorder(root):
    current = root
    while current is not None:
        if current.left is None:
            visit(current.value)
            current = current.right
        else:
            predecessor = current.left
            while predecessor.right is not None and predecessor.right != current:
                predecessor = predecessor.right
 
            if predecessor.right is None:
                predecessor.right = current
                current = current.left
            else:
                predecessor.right = None
                visit(current.value)
                current = current.right