94. Binary Tree Inorder Traversal - Explanation
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
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
1. Depth First Search
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
- Create a result list to store the node values.
- Define a recursive helper function that takes a node as input.
- If the node is
null, return immediately (base case). - Recursively call the function on the left child.
- Add the current node's value to the result list.
- Recursively call the function on the right child.
- 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 resTime & Space Complexity
- Time complexity:
- Space complexity:
- space for the recursion stack.
- space for the output array.
2. Iterative Depth First Search
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
- Initialize an empty result list and an empty stack.
- Set the current node to the root.
- While the current node is not
nullor 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.
- While the current node is not
- 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 resTime & Space Complexity
- Time complexity:
- Space complexity:
- space for the stack.
- 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
- Initialize the current node to the root.
- 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.
- 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 resTime & Space Complexity
- Time complexity:
- Space complexity:
- extra space.
- 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.
Sign in to join the discussion