Hello and welcome back!
In our last lesson, we mastered the three fundamental recursive tree traversals: preorder, inorder, and postorder. You saw how their elegant recursive structure allows us to navigate a tree with very concise code. However, we also noted that recursion has a hidden cost: the call stack. For very deep or unbalanced trees, this can lead to StackOverflowError exceptions.
This lesson addresses that limitation directly. We will focus on today's learning outcome: implementing an iterative inorder traversal using a stack. You will learn how to manually replicate the process of recursion, giving you a powerful tool for situations where an iterative solution is necessary or more efficient. This is a common and fundamental pattern in competitive programming.
1. From Recursive Magic to Iterative Logic
Recall the three steps of a recursive inorder traversal:
- Recursively traverse the left subtree.
- Visit the root node.
- Recursively traverse the right subtree.
The "magic" of recursion is that the programming language manages a call stack behind the scenes to remember where to return after each function call. When we go from root to root.left, the call stack remembers the root node so we can come back to it later.
Our goal with an iterative solution is to replace this implicit call stack with an explicit stack data structure that we control. We will use a simple Python list as our stack. This stack will store the nodes we need to visit later, perfectly mimicking the "pause and resume" nature of recursion.
2. The Core Algorithm: Simulating Recursion with a Stack
The logic of iterative inorder traversal hinges on a simple process that we repeat until all nodes are visited.

The algorithm works as follows:
- Start with the
rootas yourcurrentnode and an emptystack. - Go as far left as possible: As long as the
currentnode is notNone, push it onto thestackand move to its left child (current = current.left). This saves the path of nodes we need to revisit. - Visit and go right: When
currentbecomesNone, it means we've hit the end of a leftward path. Now, it's time to process a node.- Pop a node from the
stack. This is the node we'll "visit" (e.g., add its value to our result list). - After visiting, set the
currentpointer to the popped node's right child.
- Pop a node from the
- Repeat: Go back to step 2 with the new
currentnode. The entire process continues as long as there are nodes to process (i.e.,currentis notNoneor thestackis not empty).
This can be tricky to grasp just from text. The following video gives an excellent conceptual walkthrough, simulating both the recursive call stack and the manual iterative stack.
Iterative & Recursive - Binary Tree Inorder Traversal - Leetcode 94 - Python
Watch this section from the NeetCode video to see a brilliant visualization of how the iterative algorithm simulates recursion. Pay close attention to how the stack is used to 'remember' where to go next.
Watch the segment from 04:36 to 11:36. The presenter uses a clear example to trace the state of the stack and the current pointer throughout the traversal.
3. Step-by-Step Implementation in Python
Now that you have the conceptual foundation, let's look at how this translates into Python code. The logic directly follows the steps we just outlined.
The following resource provides a clean implementation and a breakdown of the algorithm.
Binary Tree Inorder Traversal in Python (Optimized Solution)
This article from Algocademy presents the complete, optimized iterative solution. We will use its structure for our implementation.
Focus on the 'Optimized Solution' section and the 'Algorithm' breakdown. Compare the code provided with the conceptual steps you just learned. We'll use this exact code structure.
Here is the code, which you should now be able to recognize:
# Assume this TreeNode class is defined
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def inorder_traversal_iterative(root: TreeNode):
result = []
stack = []
current = root
# The loop continues as long as there's something to process
while current is not None or stack:
# 1. Go as far left as possible, pushing nodes onto the stack
while current is not None:
stack.append(current)
current = current.left
# At this point, current is None. We need to process a node from the stack.
# 2. Pop the last node we saved
current = stack.pop()
# 3. "Visit" the node by adding its value to the result
result.append(current.val)
# 4. Now, try to explore the right subtree of the node we just visited
current = current.right
return result
To see this code being built and explained live, you can watch the final part of the NeetCode video.
Iterative & Recursive - Binary Tree Inorder Traversal - Leetcode 94 - Python
This part of the video walks through writing the Python code we just discussed. It's a great way to reinforce the logic.
Watch from 11:36 to 14:29 to see the code being implemented and explained.
Test your understanding!
Let's use the tree from our previous lesson:
F
/ \
B G
/ \ \
A D I
/ \ /
C E H
Trace the iterative inorder traversal. What is the state of the stack and the result list right after the node D is popped from the stack and its value is added to the result?
Show answer
- Initial state:
current= F,stack= [],result= [] - Go left:
stackbecomes [F, B, A].currentbecomesNone. - Pop A:
current= A.result= ['A'].currentbecomes A.right (None). - Pop B:
current= B.result= ['A', 'B'].currentbecomes B.right (D). - Go left from D:
stackbecomes [F, D, C].currentbecomesNone. - Pop C:
current= C.result= ['A', 'B', 'C'].currentbecomes C.right (None). - Pop D:
current= D.result= ['A', 'B', 'C', 'D'].
At this point, the state is:
stack:[F](Node F is still on the stack, waiting for its entire left subtree to be processed).result:['A', 'B', 'C', 'D']- The code will next set
currenttoD.right, which is nodeE.
4. Complexity Analysis
A key reason to learn different algorithm variations in competitive programming is to understand their performance trade-offs.
-
Time Complexity:
Each node in the tree is pushed onto the stack exactly once and popped exactly once. Therefore, we visit each node a constant number of times, making the time complexity linear with respect to the number of nodes, . -
Space Complexity:
The space required is determined by the maximum number of elements in ourstackat any one time. This occurs when we traverse the longest path from the root down to a leaf. In the worst case (a completely skewed tree, like a linked list), all nodes might be on the stack. In the best case (a perfectly balanced tree), the height is about .- Worst Case:
- Average/Best Case:
Notice that the space complexity is identical to the recursive solution. The primary advantage of the iterative method is not a reduction in theoretical worst-case space but the avoidance of the system's hard recursion depth limit, making it more robust for very deep trees.
Conclusion
Congratulations! You've now implemented one of the most fundamental tree algorithms both recursively and iteratively. Understanding how to manually manage a stack to simulate recursion is a skill that will be invaluable for many other problems in competitive programming.
Key Takeaways:
- Iterative inorder traversal uses an explicit
stackto replicate the behavior of the recursive call stack. - The core logic is a loop that repeatedly:
- Travels to the leftmost node, pushing parent nodes onto the stack.
- Pops a node, "visits" it, and then moves to its right child.
- This process continues until both the
currentpointer is null and thestackis empty. - The time complexity is and the space complexity is , where is the tree's height.
Up Next:
We have thoroughly explored tree traversals using a depth-first approach (going deep before going wide), using both recursion and a stack. In our next lesson, we will shift gears to a different traversal strategy: level-order traversal. This is a breadth-first approach that explores the tree level by level, and it uses a queue instead of a stack.