Hello! Welcome back to our module on binary trees.
In the last lesson, we mastered the three fundamental recursive DFS traversals: pre-order, in-order, and post-order. We saw how recursion elegantly handles tree exploration by using the implicit call stack to manage state.
Today, we're going to address a very common interview follow-up question: "Can you do that iteratively?" Your goal is to implement pre-order and in-order traversals using an explicit stack. This is a crucial skill that demonstrates a deeper understanding of how these traversals work "under the hood." It also provides a solution that avoids the risk of a stack overflow error on extremely deep trees.
1. From Recursion to Iteration: The Role of the Stack
The transition from a recursive to an iterative solution involves replacing the implicit function call stack with an explicit stack data structure that you manage yourself. The core idea remains the same: we need a way to "remember" which nodes to visit later.
- In pre-order (
Root -> Left -> Right), we process a node and then need to remember to visit its children, left first, then right. - In in-order (
Left -> Root -> Right), we can't process a node until its entire left subtree has been visited. The stack helps us keep track of the path of "parent" nodes as we descend to the leftmost leaf.
Let's explore the pattern for each traversal.
2. Iterative Pre-order Traversal (Root -> Left -> Right)
The iterative pre-order traversal has a very direct and intuitive implementation using a stack. Since a stack is a Last-In, First-Out (LIFO) data structure, we can leverage this property to ensure we process the left child before the right child.
The algorithm is as follows:
- Initialize an empty list for the results and an empty stack.
- If the root is null, return the empty list. Otherwise, push the root node onto the stack.
- Loop as long as the stack is not empty:
a. Pop a node from the stack. Let's call itcurrent.
b. Addcurrent.valto the results list.
c. Push the right child first, if it exists.
d. Push the left child second, if it exists.
By pushing the right child before the left child, the LIFO nature of the stack guarantees that the left child will be on top, ready to be popped and processed in the next iteration.
The following resource provides an excellent and concise explanation of this algorithm, along with code implementations in C++, Java, Python, and JavaScript.
Iterative Preorder Traversal of Binary Tree - Tutorial
The article 'Iterative Preorder Traversal of Binary Tree' from takeuforward breaks down this common interview pattern clearly. Focus on the algorithm and the Python implementation.
Read the 'Algorithm' section for a step-by-step breakdown. Then, review the 'Python' code implementation to see how it translates directly. Finally, review the 'Complexity Analysis' at the end.
Here's a summary of the Python code you just reviewed, which perfectly implements this pattern:
def preorderTraversal(root):
if not root:
return []
stack = [root]
result = []
while stack:
node = stack.pop()
result.append(node.val) # 1. Process node
if node.right:
stack.append(node.right) # 2. Push right child
if node.left:
stack.append(node.left) # 3. Push left child
return result
This image provides a helpful visual trace of the process:

3. Iterative In-order Traversal (Left -> Root -> Right)
The iterative in-order traversal requires a slightly more complex pattern because we cannot process a node until we have finished its entire left subtree. This means we must first travel all the way down to the leftmost node.
The algorithm for this is a two-phase loop:
- Initialize an empty list for the results, an empty stack, and a
currentpointer set to theroot. - Loop as long as
currentis notNoneOR the stack is not empty:
a. Phase 1: Go Left. Whilecurrentis notNone, push it onto the stack and move to its left child (current = current.left). This loop continues until we find aNoneleft child, loading up the stack with the path we took.
b. Phase 2: Process and Go Right. OncecurrentisNone, it means we've hit the end of a leftward path. Now we pop a node from the stack.
i. This popped node is the next one to be processed in the in-order sequence. Add its value to the results.
ii. Setcurrentto the popped node's right child (current = node.right). The main loop will then begin again from Phase 1 for this newcurrentnode.
This video from NeetCode provides an outstanding conceptual walkthrough and coding demonstration of this exact pattern.
Iterative & Recursive - Binary Tree Inorder Traversal - Leetcode 94 - Python
Watch this video to see a detailed simulation of the iterative in-order traversal algorithm. The visualization of the stack and the 'current' pointer is key to understanding the logic.
Watch from 04:46 to 14:29. The first part (until 11:45) is a conceptual simulation, and the second part shows the Python implementation. Pay close attention to how the algorithm first pushes all left nodes, then pops, processes, and moves right.
This image also gives a great step-by-step trace of the process, showing how the stack and the current node interact.

The resulting Python code looks like this:
def inorderTraversal(root):
stack = []
result = []
curr = root
while curr or stack:
# Phase 1: Go left and push nodes onto the stack
while curr:
stack.append(curr)
curr = curr.left
# Phase 2: Pop, process the node, and go right
curr = stack.pop()
result.append(curr.val)
curr = curr.right
return result
Test your understanding!
Consider the following binary tree:
10
/ \
5 15
/ \ \
2 7 20
What is the state of the stack just before the node with value 10 is added to the result list during an iterative in-order traversal?
Show answer
The in-order traversal visits nodes in the order: 2, 5, 7, 10, 15, 20.
Let's trace the algorithm:
curris 10. Push 10.currbecomes 5.curris 5. Push 5.currbecomes 2.curris 2. Push 2.currbecomesNone.- Stack:
[10, 5, 2]
- Stack:
currisNone. Pop 2, process it (result= [2]).currbecomesNone(2's right child).currisNone. Pop 5, process it (result= [2, 5]).currbecomes 7.curris 7. Push 7.currbecomesNone.- Stack:
[10, 7]
- Stack:
currisNone. Pop 7, process it (result= [2, 5, 7]).currbecomesNone.currisNone. Pop 10. This is the moment right before 10 is processed.
At this point, the stack is empty because 10 was the last element.
Answer: The stack is empty ([]). The node 10 was just popped from it.
Conclusion
Excellent work! You have now learned how to implement pre-order and in-order traversals both recursively and iteratively. Understanding both approaches is a sign of a well-rounded developer and is frequently tested in interviews.
Key Takeaways:
- Iterative traversals replace the implicit call stack of recursion with an explicit
stackdata structure. - Iterative Pre-order has a simple pattern: Pop a node, process it, then push its right child followed by its left child onto the stack.
- Iterative In-order uses a two-phase approach: first, traverse as far left as possible while pushing nodes onto the stack; then, pop a node, process it, and move to its right child to repeat the process.
- For both traversals, the time complexity is O(N) and the space complexity is O(H) (the height of the tree), which is the same as their recursive counterparts.
In our next lesson, we will use these traversal techniques as building blocks to solve common tree problems, such as finding a tree's maximum depth, its diameter, and checking if it's balanced.