Skip to content

Commit 0dc719b

Browse files
committed
Complete Chapter 5 (Trees): BFS level order, LCA, max path sum, serialize/deserialize, iterative inorder
1 parent ab8c0ea commit 0dc719b

9 files changed

Lines changed: 1419 additions & 0 deletions

‎CodingInterviewFightClub/src/SUMMARY.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,12 @@
6565
- [4.3 Merge Two Sorted Lists](ch04-linked-lists/merge-two-sorted-lists.md)
6666
- [4.4 Remove Nth Node From End](ch04-linked-lists/remove-nth-node-from-end.md)
6767
- [4.5 Linked List Cycle II](ch04-linked-lists/linked-list-cycle-ii.md)
68+
69+
- [5. Trees](ch05-trees/index.md)
70+
- [5.0 Pattern Primer: The Recursive Data Structure](ch05-trees/pattern-primer.md)
71+
- [5.1 Maximum Depth Of Binary Tree](ch05-trees/maximum-depth-of-binary-tree.md)
72+
- [5.2 Binary Tree Level Order Traversal](ch05-trees/binary-tree-level-order-traversal.md)
73+
- [5.3 Lowest Common Ancestor](ch05-trees/lowest-common-ancestor.md)
74+
- [5.4 Binary Tree Maximum Path Sum](ch05-trees/binary-tree-maximum-path-sum.md)
75+
- [5.5 Serialize And Deserialize Binary Tree](ch05-trees/serialize-and-deserialize-binary-tree.md)
76+
- [5.6 Binary Tree Inorder Traversal (Iterative)](ch05-trees/binary-tree-inorder-traversal-iterative.md)
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
# 5.6 Binary Tree Inorder Traversal (Iterative)
2+
3+
> **Source:** [`src/main/kotlin/tree/BInaryTreeInOrderTraversalIterative.kt`](https://github.com/arpanpathak/AdvancedAlgorithmPatterns/blob/main/src/main/kotlin/tree/BInaryTreeInOrderTraversalIterative.kt)
4+
> **Pattern:** explicit stack · **Core page**
5+
6+
## The Problem
7+
8+
Given the root of a binary tree, return the **in-order traversal** of its nodes' values — left subtree, node, right subtree — **without recursion**.
9+
10+
- Constraints: $0 \le n \le 100$ (LeetCode), but the *point* is trees large or skewed enough that the $O(h)$ call stack would be a real risk.
11+
12+
## Examples
13+
14+
```
15+
Input: 1
16+
\
17+
2
18+
/
19+
3
20+
Output: [1, 3, 2]
21+
22+
Input: root = [] -> Output: []
23+
Input: root = [1] -> Output: [1]
24+
```
25+
26+
## Intuition — the call stack becomes an explicit stack
27+
28+
Recursion's in-order is deceptively simple: `inorder(node) = inorder(node.left); visit(node); inorder(node.right)`. The recursion *implicitly* uses the call stack to remember "I was here, now resume with the right subtree." The iterative version must build that same memory **by hand** — which is exactly why the interviewer asks.
29+
30+
The shape of the stack is the insight: you never visit a node when you first see it — you **push** it and keep going left. Only when you can't go left anymore do you **pop** and visit, then step right. In terms of the [traversal table](pattern-primer.md): in-order visits the left spine first, then the node, then the right spine — and the stack *is* the spine.
31+
32+
**The two-phase loop:** the outer `while (current != null || stack.isNotEmpty())` has two inner phases — a *descend* phase (push and go left) and a *visit* phase (pop, record, jump right). Each node is pushed exactly once and popped exactly once, so the whole walk is $O(n)$.
33+
34+
## Approach 1 — Recursion (baseline, for contrast)
35+
36+
```kotlin
37+
fun inorder(root: TreeNode?): List<Int> = root?.let {
38+
inorder(it.left) + listOf(it.`val`) + inorder(it.right)
39+
} ?: emptyList()
40+
```
41+
42+
Two lines, obviously correct — and exactly why this problem exists: the *implicit* stack costs $O(h)$ real call-stack memory, which overflows on a skewed tree of ~$10^5$ nodes. The iterative version below is the same algorithm with the stack made explicit and moved to the heap.
43+
44+
## Approach 2 — Explicit-stack simulation (the repo's version, optimal)
45+
46+
```kotlin
47+
class BInaryTreeInOrderTraversalIterative {
48+
/**
49+
* @param root the root of the binary tree
50+
* @return the in-order (left, node, right) node values
51+
*/
52+
fun inorderTraversal(root: TreeNode?): List<Int> {
53+
val stack = ArrayDeque<TreeNode>()
54+
val result = mutableListOf<Int>()
55+
var current = root
56+
57+
while (current != null || stack.isNotEmpty()) {
58+
// Traverse to the leftmost node
59+
while (current != null) {
60+
stack.addLast(current)
61+
current = current.left
62+
}
63+
64+
// Visit the node
65+
current = stack.removeLast()
66+
result.add(current.`val`)
67+
68+
// Move to the right subtree
69+
current = current.right
70+
}
71+
return result
72+
}
73+
}
74+
```
75+
76+
```java
77+
import java.util.*;
78+
79+
public class BinaryTreeInorderTraversalIterative {
80+
/**
81+
* @param root the root of the binary tree
82+
* @return the in-order (left, node, right) node values
83+
*/
84+
public List<Integer> inorderTraversal(TreeNode root) {
85+
Deque<TreeNode> stack = new ArrayDeque<>();
86+
List<Integer> result = new ArrayList<>();
87+
TreeNode current = root;
88+
89+
while (current != null || !stack.isEmpty()) {
90+
while (current != null) { // descend the left spine
91+
stack.push(current);
92+
current = current.left;
93+
}
94+
current = stack.pop(); // leftmost unvisited node
95+
result.add(current.val); // visit
96+
current = current.right; // now its right subtree
97+
}
98+
return result;
99+
}
100+
}
101+
```
102+
103+
```cpp
104+
#include <vector>
105+
#include <stack>
106+
107+
class BInaryTreeInOrderTraversalIterative {
108+
public:
109+
/**
110+
* @param root the root of the binary tree
111+
* @return the in-order (left, node, right) node values
112+
*/
113+
std::vector<int> inorderTraversal(TreeNode* root) {
114+
std::vector<int> result;
115+
std::stack<TreeNode*> st;
116+
TreeNode* current = root;
117+
118+
while (current != nullptr || !st.empty()) {
119+
while (current != nullptr) { // descend the left spine
120+
st.push(current);
121+
current = current->left;
122+
}
123+
current = st.top();
124+
st.pop();
125+
result.push_back(current->val); // visit
126+
current = current->right; // now its right subtree
127+
}
128+
return result;
129+
}
130+
};
131+
```
132+
133+
```python
134+
def inorder_traversal(root: TreeNode | None) -> list[int]:
135+
"""
136+
@param root: the root of the binary tree
137+
@return: the in-order (left, node, right) node values
138+
"""
139+
stack: list[TreeNode] = []
140+
result: list[int] = []
141+
current = root
142+
143+
while current is not None or stack:
144+
while current is not None: # descend the left spine
145+
stack.append(current)
146+
current = current.left
147+
current = stack.pop() # leftmost unvisited node
148+
result.append(current.val) # visit
149+
current = current.right # now its right subtree
150+
return result
151+
```
152+
153+
```rust
154+
impl Solution {
155+
/// @param root the root of the binary tree
156+
/// @return the in-order (left, node, right) node values
157+
pub fn inorder_traversal(root: Option<Rc<RefCell<TreeNode>>>) -> Vec<i32> {
158+
let mut stack: Vec<Rc<RefCell<TreeNode>>> = Vec::new();
159+
let mut result = Vec::new();
160+
let mut current = root;
161+
162+
while current.is_some() || !stack.is_empty() {
163+
while let Some(node) = current { // descend the left spine
164+
stack.push(node.clone());
165+
current = node.borrow().left.clone();
166+
}
167+
let node = stack.pop().unwrap(); // leftmost unvisited node
168+
result.push(node.borrow().val); // visit
169+
current = node.borrow().right.clone(); // now its right subtree
170+
}
171+
result
172+
}
173+
}
174+
```
175+
176+
> **Rust note:** `stack` holds `Rc` clones (cheap refcount bumps); each node is cloned on push and dropped after pop — net effect: every node alive on the stack exactly once, mirroring the other languages.
177+
178+
## Dry run
179+
180+
**Input:** the tree above (`1 -> right 2 -> left 3`).
181+
182+
```
183+
stack=[], current=1, result=[]
184+
descend: push 1, current=null stack=[1]
185+
visit: pop 1, result=[1], current=2
186+
descend: push 2, current=3; push 3, current=null stack=[2,3]
187+
visit: pop 3, result=[1,3], current=null
188+
visit: pop 2, result=[1,3,2], current=null
189+
stack empty, current=null -> stop
190+
Output: [1, 3, 2] ✓
191+
```
192+
193+
A nice property of in-order on a BST: the result is the *sorted* order — the same `[1, 2, 3]`-style sequence you'd get from a sorted array, which is why "in-order = sorted" is the standard BST litmus test.
194+
195+
## Complexity
196+
197+
**Time.** Each node pushed once, popped once:
198+
199+
$$
200+
T(n) = O(n)
201+
$$
202+
203+
**Space.** The stack holds at most one left-spine at a time — worst case is a skewed tree:
204+
205+
$$
206+
S(n) = O(h) \text{ on the heap (no call-stack risk)}, \quad h \in [\log n, n]
207+
$$
208+
209+
This is the whole point of the problem: identical asymptotics to recursion, but the memory lives on the *heap*, so a $10^5$-node skewed tree runs where recursion would `StackOverflowError`.
210+
211+
## Variants & follow-ups
212+
213+
- **Iterative pre/post-order** — same skeleton, different visit timing: pre-order visits at *push* time; post-order needs a "was I here before?" marker (two-stack or reversed-pre-order tricks).
214+
- **Morris Traversal** — $O(1)$ space by temporarily *threading* right pointers: if a node's left subtree has no rightmost node yet, wire it to the current node, walk left; when you return, unthread and visit. Interviewers rarely demand it — mention it as the "constant-space curiosity."
215+
- **BST Iterator** (`src/main/kotlin/tree/bst/`) — this exact loop turned into `hasNext()` / `next()`: the stack *is* the iterator state, and each `next()` does one descend+visit.
216+
- **Kth Smallest Element In A BST** — run this traversal and stop at the $k$-th visit.
217+
- **Interview follow-up:** "Recursive version?" Write it first — two lines, obviously correct — *then* offer this page as the stack-safe refinement. Showing both and explaining the tradeoff is the answer.

0 commit comments

Comments
 (0)