Binary Tree Inorder Traversal

Traverse a binary tree in inorder (left -> root -> right)

Language Selection

Choose your preferred programming language

Showing: Python

Problem Statement

Given the root of a binary tree, return the inorder traversal of its nodes’ values. In inorder traversal, we visit nodes in the following order: left subtree -> root -> right subtree.

Input:

  • Root of a binary tree

Output:

  • List of integers representing the inorder traversal

Constraints:

  • The number of nodes in the tree is in the range [0, 100]
  • -100 ≤ Node.val ≤ 100

Examples:

Example 1:

    1
     \
      2
     /
    3

Input: root = [1,null,2,3,null,null,null]
Output: [1,3,2]

Example 2:

Input: root = []
Output: []

Example 3:

Input: root = [1]
Output: [1]

Example 4:

      1
     / \
    2   3
   / \
  4   5

Input: root = [1,2,3,4,5]
Output: [4,2,5,1,3]
Explanation: Visit left subtree(4,2,5), then root(1), then right subtree(3)

Solutions

Approach 1: Recursive Solution

The most intuitive approach using recursion following the natural definition of inorder traversal.

Algorithm:

  1. Recursively traverse the left subtree
  2. Visit the root node (add to result)
  3. Recursively traverse the right subtree

Python:

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def inorderTraversal(root):
    """
    Recursive inorder traversal
    Time: O(N) where N is number of nodes
    Space: O(H) where H is height of tree (recursion stack)
    """
    result = []
    
    def dfs(node):
        if not node:
            return
        
        dfs(node.left)             # Traverse left subtree
        result.append(node.val)    # Visit root
        dfs(node.right)            # Traverse right subtree
    
    dfs(root)
    return result

def inorderTraversalCompact(root):
    """
    Compact recursive solution
    Time: O(N)
    Space: O(H)
    """
    if not root:
        return []
    
    return (inorderTraversalCompact(root.left) + 
            [root.val] + 
            inorderTraversalCompact(root.right))

Java:

import java.util.*;

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

class Solution {
    /**
     * Recursive inorder traversal
     * Time: O(N)
     * Space: O(H)
     */
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        dfs(root, result);
        return result;
    }
    
    private void dfs(TreeNode node, List<Integer> result) {
        if (node == null) {
            return;
        }
        
        dfs(node.left, result);      // Traverse left subtree
        result.add(node.val);        // Visit root
        dfs(node.right, result);     // Traverse right subtree
    }
    
    /**
     * Compact recursive solution
     * Time: O(N)
     * Space: O(H)
     */
    public List<Integer> inorderTraversalCompact(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null) {
            return result;
        }
        
        result.addAll(inorderTraversalCompact(root.left));
        result.add(root.val);
        result.addAll(inorderTraversalCompact(root.right));
        
        return result;
    }
}

Go:

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

// inorderTraversal - Recursive inorder traversal
// Time: O(N)
// Space: O(H)
func inorderTraversal(root *TreeNode) []int {
    result := []int{}
    
    var dfs func(node *TreeNode)
    dfs = func(node *TreeNode) {
        if node == nil {
            return
        }
        
        dfs(node.Left)                         // Traverse left subtree
        result = append(result, node.Val)      // Visit root
        dfs(node.Right)                        // Traverse right subtree
    }
    
    dfs(root)
    return result
}

// inorderTraversalCompact - Compact recursive solution
// Time: O(N)
// Space: O(H)
func inorderTraversalCompact(root *TreeNode) []int {
    if root == nil {
        return []int{}
    }
    
    result := inorderTraversalCompact(root.Left)
    result = append(result, root.Val)
    result = append(result, inorderTraversalCompact(root.Right)...)
    
    return result
}

JavaScript:

class TreeNode {
    constructor(val, left, right) {
        this.val = (val === undefined ? 0 : val);
        this.left = (left === undefined ? null : left);
        this.right = (right === undefined ? null : right);
    }
}

/**
 * Recursive inorder traversal
 * Time: O(N)
 * Space: O(H)
 */
function inorderTraversal(root) {
    const result = [];
    
    function dfs(node) {
        if (!node) {
            return;
        }
        
        dfs(node.left);           // Traverse left subtree
        result.push(node.val);    // Visit root
        dfs(node.right);          // Traverse right subtree
    }
    
    dfs(root);
    return result;
}

/**
 * Compact recursive solution
 * Time: O(N)
 * Space: O(H)
 */
function inorderTraversalCompact(root) {
    if (!root) {
        return [];
    }
    
    return [...inorderTraversalCompact(root.left),
            root.val,
            ...inorderTraversalCompact(root.right)];
}

C#:

using System;
using System.Collections.Generic;

public class TreeNode {
    public int val;
    public TreeNode left;
    public TreeNode right;
    public TreeNode(int val = 0, TreeNode left = null, TreeNode right = null) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

public class Solution {
    /// <summary>
    /// Recursive inorder traversal
    /// Time: O(N)
    /// Space: O(H)
    /// </summary>
    public IList<int> InorderTraversal(TreeNode root) {
        List<int> result = new List<int>();
        Dfs(root, result);
        return result;
    }
    
    private void Dfs(TreeNode node, List<int> result) {
        if (node == null) {
            return;
        }
        
        Dfs(node.left, result);      // Traverse left subtree
        result.Add(node.val);        // Visit root
        Dfs(node.right, result);     // Traverse right subtree
    }
    
    /// <summary>
    /// Compact recursive solution
    /// Time: O(N)
    /// Space: O(H)
    /// </summary>
    public IList<int> InorderTraversalCompact(TreeNode root) {
        List<int> result = new List<int>();
        if (root == null) {
            return result;
        }
        
        result.AddRange(InorderTraversalCompact(root.left));
        result.Add(root.val);
        result.AddRange(InorderTraversalCompact(root.right));
        
        return result;
    }
}

Approach 2: Iterative Solution using Stack

Use an explicit stack to simulate the recursion call stack. This is more complex for inorder than preorder.

Algorithm:

  1. Use a stack to track nodes
  2. Start with root and go as far left as possible, pushing nodes to stack
  3. When no more left children, pop from stack, visit node, and go to right child
  4. Repeat until stack is empty and no current node

Python:

def inorderTraversalIterative(root):
    """
    Iterative inorder traversal using stack
    Time: O(N)
    Space: O(H)
    """
    result = []
    stack = []
    current = root
    
    while stack or current:
        # Go to the leftmost node
        while current:
            stack.append(current)
            current = current.left
        
        # Current is None here, so pop from stack
        current = stack.pop()
        result.append(current.val)  # Visit node
        
        # Move to right subtree
        current = current.right
    
    return result

def inorderTraversalIterativeV2(root):
    """
    Alternative iterative approach with explicit state tracking
    Time: O(N)
    Space: O(H)
    """
    if not root:
        return []
    
    result = []
    stack = [(root, False)]  # (node, is_visited)
    
    while stack:
        node, visited = stack.pop()
        
        if visited:
            # Node has been processed, add to result
            result.append(node.val)
        else:
            # Process node: right, node (visited), left
            if node.right:
                stack.append((node.right, False))
            
            stack.append((node, True))  # Mark as visited
            
            if node.left:
                stack.append((node.left, False))
    
    return result

Java:

public List<Integer> inorderTraversalIterative(TreeNode root) {
    /**
     * Iterative inorder traversal using stack
     * Time: O(N)
     * Space: O(H)
     */
    List<Integer> result = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<>();
    TreeNode current = root;
    
    while (!stack.isEmpty() || current != null) {
        // Go to the leftmost node
        while (current != null) {
            stack.push(current);
            current = current.left;
        }
        
        // Current is null here, so pop from stack
        current = stack.pop();
        result.add(current.val);    // Visit node
        
        // Move to right subtree
        current = current.right;
    }
    
    return result;
}

class NodeState {
    TreeNode node;
    boolean visited;
    
    NodeState(TreeNode node, boolean visited) {
        this.node = node;
        this.visited = visited;
    }
}

public List<Integer> inorderTraversalIterativeV2(TreeNode root) {
    /**
     * Alternative iterative approach with explicit state tracking
     * Time: O(N)
     * Space: O(H)
     */
    List<Integer> result = new ArrayList<>();
    if (root == null) {
        return result;
    }
    
    Stack<NodeState> stack = new Stack<>();
    stack.push(new NodeState(root, false));
    
    while (!stack.isEmpty()) {
        NodeState current = stack.pop();
        
        if (current.visited) {
            // Node has been processed, add to result
            result.add(current.node.val);
        } else {
            // Process node: right, node (visited), left
            if (current.node.right != null) {
                stack.push(new NodeState(current.node.right, false));
            }
            
            stack.push(new NodeState(current.node, true));  // Mark as visited
            
            if (current.node.left != null) {
                stack.push(new NodeState(current.node.left, false));
            }
        }
    }
    
    return result;
}

Go:

// inorderTraversalIterative - Iterative inorder traversal using stack
// Time: O(N)
// Space: O(H)
func inorderTraversalIterative(root *TreeNode) []int {
    result := []int{}
    stack := []*TreeNode{}
    current := root
    
    for len(stack) > 0 || current != nil {
        // Go to the leftmost node
        for current != nil {
            stack = append(stack, current)
            current = current.Left
        }
        
        // Current is nil here, so pop from stack
        current = stack[len(stack)-1]
        stack = stack[:len(stack)-1]
        result = append(result, current.Val)  // Visit node
        
        // Move to right subtree
        current = current.Right
    }
    
    return result
}

type NodeState struct {
    node    *TreeNode
    visited bool
}

// inorderTraversalIterativeV2 - Alternative iterative approach with explicit state tracking
// Time: O(N)
// Space: O(H)
func inorderTraversalIterativeV2(root *TreeNode) []int {
    if root == nil {
        return []int{}
    }
    
    result := []int{}
    stack := []NodeState{{root, false}}
    
    for len(stack) > 0 {
        current := stack[len(stack)-1]
        stack = stack[:len(stack)-1]
        
        if current.visited {
            // Node has been processed, add to result
            result = append(result, current.node.Val)
        } else {
            // Process node: right, node (visited), left
            if current.node.Right != nil {
                stack = append(stack, NodeState{current.node.Right, false})
            }
            
            stack = append(stack, NodeState{current.node, true})  // Mark as visited
            
            if current.node.Left != nil {
                stack = append(stack, NodeState{current.node.Left, false})
            }
        }
    }
    
    return result
}

JavaScript:

/**
 * Iterative inorder traversal using stack
 * Time: O(N)
 * Space: O(H)
 */
function inorderTraversalIterative(root) {
    const result = [];
    const stack = [];
    let current = root;
    
    while (stack.length > 0 || current) {
        // Go to the leftmost node
        while (current) {
            stack.push(current);
            current = current.left;
        }
        
        // Current is null here, so pop from stack
        current = stack.pop();
        result.push(current.val);   // Visit node
        
        // Move to right subtree
        current = current.right;
    }
    
    return result;
}

/**
 * Alternative iterative approach with explicit state tracking
 * Time: O(N)
 * Space: O(H)
 */
function inorderTraversalIterativeV2(root) {
    if (!root) {
        return [];
    }
    
    const result = [];
    const stack = [[root, false]];  // [node, isVisited]
    
    while (stack.length > 0) {
        const [node, visited] = stack.pop();
        
        if (visited) {
            // Node has been processed, add to result
            result.push(node.val);
        } else {
            // Process node: right, node (visited), left
            if (node.right) {
                stack.push([node.right, false]);
            }
            
            stack.push([node, true]);  // Mark as visited
            
            if (node.left) {
                stack.push([node.left, false]);
            }
        }
    }
    
    return result;
}

C#:

public IList<int> InorderTraversalIterative(TreeNode root) {
    /// <summary>
    /// Iterative inorder traversal using stack
    /// Time: O(N)
    /// Space: O(H)
    /// </summary>
    List<int> result = new List<int>();
    Stack<TreeNode> stack = new Stack<TreeNode>();
    TreeNode current = root;
    
    while (stack.Count > 0 || current != null) {
        // Go to the leftmost node
        while (current != null) {
            stack.Push(current);
            current = current.left;
        }
        
        // Current is null here, so pop from stack
        current = stack.Pop();
        result.Add(current.val);    // Visit node
        
        // Move to right subtree
        current = current.right;
    }
    
    return result;
}

public IList<int> InorderTraversalIterativeV2(TreeNode root) {
    /// <summary>
    /// Alternative iterative approach with explicit state tracking
    /// Time: O(N)
    /// Space: O(H)
    /// </summary>
    List<int> result = new List<int>();
    if (root == null) {
        return result;
    }
    
    Stack<(TreeNode node, bool visited)> stack = new Stack<(TreeNode, bool)>();
    stack.Push((root, false));
    
    while (stack.Count > 0) {
        var (node, visited) = stack.Pop();
        
        if (visited) {
            // Node has been processed, add to result
            result.Add(node.val);
        } else {
            // Process node: right, node (visited), left
            if (node.right != null) {
                stack.Push((node.right, false));
            }
            
            stack.Push((node, true));  // Mark as visited
            
            if (node.left != null) {
                stack.Push((node.left, false));
            }
        }
    }
    
    return result;
}

Approach 3: Morris Traversal (Space Optimized)

Use Morris traversal to achieve O(1) space complexity by creating temporary links.

Python:

def inorderTraversalMorris(root):
    """
    Morris inorder traversal with O(1) space
    Time: O(N)
    Space: O(1)
    """
    result = []
    current = root
    
    while current:
        if not current.left:
            # No left child, visit and go right
            result.append(current.val)
            current = current.right
        else:
            # Find inorder predecessor
            predecessor = current.left
            while predecessor.right and predecessor.right != current:
                predecessor = predecessor.right
            
            if not predecessor.right:
                # First time visiting, create link
                predecessor.right = current
                current = current.left
            else:
                # Second time visiting, remove link and visit
                predecessor.right = None
                result.append(current.val)
                current = current.right
    
    return result

Java:

public List<Integer> inorderTraversalMorris(TreeNode root) {
    /**
     * Morris inorder traversal with O(1) space
     * Time: O(N)
     * Space: O(1)
     */
    List<Integer> result = new ArrayList<>();
    TreeNode current = root;
    
    while (current != null) {
        if (current.left == null) {
            // No left child, visit and go right
            result.add(current.val);
            current = current.right;
        } else {
            // Find inorder predecessor
            TreeNode predecessor = current.left;
            while (predecessor.right != null && predecessor.right != current) {
                predecessor = predecessor.right;
            }
            
            if (predecessor.right == null) {
                // First time visiting, create link
                predecessor.right = current;
                current = current.left;
            } else {
                // Second time visiting, remove link and visit
                predecessor.right = null;
                result.add(current.val);
                current = current.right;
            }
        }
    }
    
    return result;
}

Go:

// inorderTraversalMorris - Morris inorder traversal with O(1) space
// Time: O(N)
// Space: O(1)
func inorderTraversalMorris(root *TreeNode) []int {
    result := []int{}
    current := root
    
    for current != nil {
        if current.Left == nil {
            // No left child, visit and go right
            result = append(result, current.Val)
            current = current.Right
        } else {
            // Find inorder predecessor
            predecessor := current.Left
            for predecessor.Right != nil && predecessor.Right != current {
                predecessor = predecessor.Right
            }
            
            if predecessor.Right == nil {
                // First time visiting, create link
                predecessor.Right = current
                current = current.Left
            } else {
                // Second time visiting, remove link and visit
                predecessor.Right = nil
                result = append(result, current.Val)
                current = current.Right
            }
        }
    }
    
    return result
}

JavaScript:

/**
 * Morris inorder traversal with O(1) space
 * Time: O(N)
 * Space: O(1)
 */
function inorderTraversalMorris(root) {
    const result = [];
    let current = root;
    
    while (current) {
        if (!current.left) {
            // No left child, visit and go right
            result.push(current.val);
            current = current.right;
        } else {
            // Find inorder predecessor
            let predecessor = current.left;
            while (predecessor.right && predecessor.right !== current) {
                predecessor = predecessor.right;
            }
            
            if (!predecessor.right) {
                // First time visiting, create link
                predecessor.right = current;
                current = current.left;
            } else {
                // Second time visiting, remove link and visit
                predecessor.right = null;
                result.push(current.val);
                current = current.right;
            }
        }
    }
    
    return result;
}

C#:

public IList<int> InorderTraversalMorris(TreeNode root) {
    /// <summary>
    /// Morris inorder traversal with O(1) space
    /// Time: O(N)
    /// Space: O(1)
    /// </summary>
    List<int> result = new List<int>();
    TreeNode current = root;
    
    while (current != null) {
        if (current.left == null) {
            // No left child, visit and go right
            result.Add(current.val);
            current = current.right;
        } else {
            // Find inorder predecessor
            TreeNode predecessor = current.left;
            while (predecessor.right != null && predecessor.right != current) {
                predecessor = predecessor.right;
            }
            
            if (predecessor.right == null) {
                // First time visiting, create link
                predecessor.right = current;
                current = current.left;
            } else {
                // Second time visiting, remove link and visit
                predecessor.right = null;
                result.Add(current.val);
                current = current.right;
            }
        }
    }
    
    return result;
}

Key Insights

  1. Visit Order: Inorder visits left subtree, then root, then right subtree (Left → Root → Right)
  2. BST Property: For BST, inorder traversal returns sorted sequence
  3. Iterative Complexity: More complex than preorder due to delayed visiting
  4. Stack Pattern: Push left nodes first, then process and move right

Edge Cases

  1. Empty tree: Return empty list
  2. Single node: Return list with one element
  3. Left skewed tree: Deep recursion/stack
  4. Right skewed tree: Shallow recursion/stack
  5. BST: Results in sorted order

Common Mistakes

  1. Wrong visit order: Visiting root before left subtree
  2. Stack management: Not properly handling the left-first traversal
  3. Morris implementation: Incorrect predecessor finding or link management
  4. Base case: Not handling null nodes properly
  5. BST assumption: Assuming all trees are BSTs

Follow-up Questions

  1. BST validation: Use inorder to check if tree is valid BST
  2. Kth smallest: Find kth smallest element in BST using inorder
  3. Recovery: Recover BST where two nodes were swapped
  4. Predecessor/Successor: Find inorder predecessor/successor
  5. Range queries: Get values in BST within a range using inorder