94. Binary Tree Inorder Traversal - Explanation

Problem Link

Description

You are given the root of a binary tree, return the inorder traversal of its nodes' values.

Example 1:

Input: root = [1,2,3,4,5,6,7]

Output: [4,2,5,1,6,3,7]

Example 2:

Input: root = [1,2,3,null,4,5,null]

Output: [2,4,1,5,3]

Example 3:

Input: root = []

Output: []

Constraints:

  • 0 <= number of nodes in the tree <= 100
  • -100 <= Node.val <= 100

Follow up: Recursive solution is trivial, could you do it iteratively?



Topics

Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Binary Tree Structure - Understanding how nodes connect via left and right children
  • Recursion - Using recursive calls to naturally traverse tree structures
  • Stacks - Simulating recursion iteratively by explicitly managing the call stack

Intuition

Inorder traversal visits nodes in the order: left subtree, current node, right subtree. For a binary search tree, this produces values in sorted order. We can use recursion to naturally handle the traversal by first recursing on the left child, then processing the current node, and finally recursing on the right child.

Algorithm

  1. Create a result list to store the node values.
  2. Define a recursive helper function that takes a node as input.
  3. If the node is null, return immediately (base case).
  4. Recursively call the function on the left child.
  5. Add the current node's value to the result list.
  6. Recursively call the function on the right child.
  7. Return the result list after traversing the entire tree.
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []

        def inorder(node):
            if not node:
                return

            inorder(node.left)
            res.append(node.val)
            inorder(node.right)

        inorder(root)
        return res

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity:
    • O(n)O(n) space for the recursion stack.
    • O(n)O(n) space for the output array.

Intuition

We can simulate the recursive call stack using an explicit stack. The key insight is that we need to go as far left as possible, then process the current node, and move to the right subtree. The stack helps us remember which nodes we still need to process after finishing the left subtrees.

Algorithm

  1. Initialize an empty result list and an empty stack.
  2. Set the current node to the root.
  3. While the current node is not null or the stack is not empty:
    • While the current node is not null, push it onto the stack and move to its left child.
    • Pop a node from the stack, add its value to the result.
    • Move to the right child of the popped node.
  4. Return the result list.
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        stack = []
        cur = root

        while cur or stack:
            while cur:
                stack.append(cur)
                cur = cur.left
            cur = stack.pop()
            res.append(cur.val)
            cur = cur.right

        return res

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity:
    • O(n)O(n) space for the stack.
    • O(n)O(n) space for the output array.

3. Morris Traversal

Intuition

Morris Traversal achieves O(1) extra space by temporarily modifying the tree structure. The idea is to create a temporary link from the rightmost node of the left subtree back to the current node. This allows us to return to the current node after traversing the left subtree without using a stack. After processing, we remove the temporary link to restore the original tree.

Algorithm

  1. Initialize the current node to the root.
  2. While the current node is not null:
    • If the current node has no left child, add its value to the result and move to the right child.
    • Otherwise, find the rightmost node in the left subtree (the inorder predecessor).
    • If the predecessor's right pointer is null, set it to the current node (create a thread) and move to the left child.
    • If the predecessor's right pointer already points to the current node, remove the thread, add the current node's value to the result, and move to the right child.
  3. Return the result list.
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        cur = root

        while cur:
            if not cur.left:
                res.append(cur.val)
                cur = cur.right
            else:
                prev = cur.left
                while prev.right and prev.right != cur:
                    prev = prev.right

                if not prev.right:
                    prev.right = cur
                    cur = cur.left
                else:
                    prev.right = None
                    res.append(cur.val)
                    cur = cur.right

        return res

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity:
    • O(1)O(1) extra space.
    • O(n)O(n) space for the output array.

Common Pitfalls

Wrong Order of Operations in Recursion

Inorder traversal requires visiting left, then current, then right. A common mistake is adding the current node's value before or after both recursive calls, which produces preorder or postorder results instead.

# Wrong: res.append(node.val) before inorder(node.left)
# Correct: inorder(node.left), then res.append(node.val)

Forgetting to Move Right in Iterative Approach

After popping and processing a node from the stack, you must move to its right child. Forgetting cur = cur.right causes an infinite loop since the same node keeps getting processed.