Trees
Expert Answer & Key Takeaways
Invert Binary Tree
root of a binary tree, invert the tree, and return its root.Examples
Level I: Recursive DFS
Intuition
Detailed Dry Run
Level II: Iterative BFS (Queue)
Intuition
Detailed Dry Run
Level III: Iterative DFS (Stack)
Intuition
Diagram
(1) Swap children of Root (2) Result after subtrees
4 4
/ \ / \
2 7 7 2
/ \ / \ / \ / \
1 3 6 9 9 6 3 1Detailed Dry Run
Maximum Depth of Binary Tree
root of a binary tree, return its maximum depth.Examples
Level I: Recursive DFS (Post-order)
Intuition
Detailed Dry Run
Level II: Iterative BFS (Level Order)
Intuition
Diagram
Level 1: [3] -> Depth 1
Level 2: [9, 20] -> Depth 2
Level 3: [15, 7] -> Depth 3Detailed Dry Run
Level III: Iterative DFS (Stack)
Intuition
(node, depth) to keep track of max depth.Detailed Dry Run
Same Tree
p and q, check if they are the same.Examples
Level I: Recursive DFS
Intuition
Diagram
(1) p.val == q.val?
(2) isSameTree(p.left, q.left)
(3) isSameTree(p.right, q.right)Detailed Dry Run
Level III: Iterative BFS (Parallel Queue)
Intuition
Detailed Dry Run
Symmetric Tree
Examples
Level I: Recursive DFS
Intuition
t1 and t2 are mirrors if:- Their roots have the same value.
t1.leftis a mirror oft2.right.t1.rightis a mirror oft2.left.
Detailed Dry Run
Level II: Iterative BFS (Queue)
Intuition
(root.left, root.right).Diagram
(1) Queue: [1.L, 1.R] -> [2, 2]
(2) Pop pair (2, 2), Push pairs: [(2.L, 2.R), (2.R, 2.L)] -> [3, 3, 4, 4]Detailed Dry Run
Level III: Iterative DFS (Two Stacks)
Intuition
Left -> Right and the other follows Right -> Left. Their values and structure must match at every step.Detailed Dry Run
Subtree of Another Tree
subRoot is a subtree of root.Examples
Level I: Recursive DFS (Is Match?)
Intuition
s is a subtree of t if:sandtare identical.sis a subtree oft.left.sis a subtree oft.right.
Detailed Dry Run
Level II: Preorder String Matching (O(N))
Intuition
subRoot is a substring of serialized root, it's a subtree.Diagram
Tree: 3(4,5) -> Preorder: ",3,4,#,#,5,#,#"
Subtree: 4 -> Preorder: ",4,#,#"
Match found in ",3,4,#,#,5,#,#"!Detailed Dry Run
Diameter of Binary Tree
Examples
Level I: Recursive DFS (Bottom-Up)
Intuition
- Depth of Left Subtree + Depth of Right Subtree.
- Diameter of Left Subtree.
- Diameter of Right Subtree. Using a bottom-up approach, we can calculate height and track the max diameter simultaneously.
Detailed Dry Run
Level II: Recursive Bottom-Up (Pair Result)
Intuition
(height, diameter_so_far). This is a cleaner functional approach.Diagram
Node(2) returns {Height: 2, Diameter: 2}
Node(3) returns {Height: 1, Diameter: 0}
Root(1) calc: max(2.H+3.H, 2.D, 3.D) -> max(3, 2, 0) = 3.Detailed Dry Run
Level III: Optimal (Iterative Post-order)
Intuition
diameter = height(left) + height(right).Logic
- Use a stack for post-order.
- Store computed heights in
Map<TreeNode, Integer>. height = 1 + max(h_left, h_right).diameter = h_left + h_right.
Detailed Dry Run
Balanced Binary Tree
Examples
Level I: Recursive DFS (Top-Down)
Intuition
Detailed Dry Run
Level II: Recursive with Memoization
Intuition
Logic
- Use a Map to store
node -> height. - In
height(node), return map value if present. - Recurse for balanced property using cached heights.
Detailed Dry Run
Level III: Optimal (Bottom-Up DFS)
Intuition
Detailed Dry Run
Construct Binary Tree from Preorder and Inorder Traversal
Level I: Recursive with Linear Search
Intuition
Thought Process
root = preorder[0].- Find
rootindex ininorderby iterating (Linear Search). - Slice
inorderintoleftInorderandrightInorder. - Recursively build left and right subtrees.
Detailed Dry Run
| Preorder | Inorder | Root | Left Subtree | Right Subtree |
|---|---|---|---|---|
| [3,9,20] | [9,3,20] | 3 | [9] | [20] |
Level II: Iterative with Stack
Intuition
Logic
- Push root to stack.
- For each next value: If
stack.top().val != inorder[inIndex], it's a left child. - Else, pop from stack until
stack.top().val != inorder[inIndex]to find the parent whose right child this value is.
Detailed Dry Run
Level III: Optimal (Recursive with Hash Map)
Intuition
Thought Process
inorder array is the primary bottleneck ( per call). We can optimize this by pre-processing the inorder array into a Hash Map that stores value -> index. This reduces the lookup time to .Diagram
Pre: [3, 9, 20, 15, 7]
In: [9, 3, 15, 20, 7]
Map: {9:0, 3:1, 15:2, 20:3, 7:4}
Step 1: Root = 3 (Index 1 in Inorder)
Left Inorder: [0..0], Right Inorder: [2..4]Detailed Dry Run
| Call | Range (Start, End) | Root Val | Map Index |
|---|---|---|---|
| Main | (0, 4) | 3 | 1 |
| Left | (0, 0) | 9 | 0 |
| Right | (2, 4) | 20 | 3 |
⚠️ Common Pitfalls & Tips
preIndex is accessed globally or passed correctly across recursive calls. Using array slicing (like preorder[1:mid+1]) is easier but often in languages like Python due to copy overhead.Validate Binary Search Tree
Level I: Inorder to Array
Intuition
Thought Process
- Perform DFS (Inorder).
- Store every
node.valin a list. - Iterate through the list and check if
list[i] < list[i+1].
Detailed Dry Run
| Node | Left Subtree | Right Subtree | Array |
|---|---|---|---|
| 2 | [1] | [3] | [1, 2, 3] |
| Result | Valid BST! |
Level II: Iterative Inorder (Stack)
Intuition
Logic
prev to the previously visited node. At each step, if current.val <= prev.val, it's not a valid BST.Detailed Dry Run
Level III: Optimal (Recursive Bounds)
Intuition
Thought Process
Pattern
Diagram
10 (range: -∞, +∞)
/ \
5 15
/ \ / \
L R L R
(5 range: -∞, 10) (15 range: 10, +∞)Detailed Dry Run
| Node | Range (Min, Max) | Valid? |
|---|---|---|
| 10 | (-∞, +∞) | YES |
| 5 | (-∞, 10) | YES |
| 15 | (10, +∞) | YES |
⚠️ Common Pitfalls & Tips
Integer.MIN_VALUE. Use null or Long for the initial bounds to handle nodes that contain the minimum possible integer value.Binary Tree Level Order Traversal
Level I: Recursive depth-first (DFS)
Intuition
Thought Process
- Maintain a list of lists
res. - Call
helper(node, level). - If
res.size() == level, add a new inner list. res.get(level).add(node.val).- Recurse left and right with
level + 1.
Detailed Dry Run
| Node | Level | Result (State) |
|---|---|---|
| 3 | 0 | [[3]] |
| 9 | 1 | [[3], [9]] |
| 20 | 1 | [[3], [9, 20]] |
| 15 | 2 | [[3], [9, 20], [15]] |
Level II: Iterative DFS (using Stack)
Intuition
(node, depth). If the result list size is equal to the current depth, we add a new empty list. Then, we add the node value to the list corresponding to its depth.Logic
- Use a Stack for DFS.
- Track
depthfor each node. res[depth].add(node.val).- Note: Push right child first, then left to maintain left-to-right order in levels.
Detailed Dry Run
| Stack | Depth | res state |
|---|---|---|
| [(3,0)] | 0 | [[3]] |
| [(20,1), (9,1)] | 1 | [[3], [9]] |
| [(20,1)] | 1 | [[3], [9, 20]] |
Level III: Optimal (BFS with Queue)
Intuition
Thought Process
Diagram
Queue: [3] -> Level nodes: [3]
Queue: [9, 20] -> Level nodes: [9, 20]
Queue: [15, 7] -> Level nodes: [15, 7]Detailed Dry Run
| Queue | Level Size | Processing | Result Area |
|---|---|---|---|
| [3] | 1 | Pop 3, Push 9, 20 | [3] |
| [9, 20] | 2 | Pop 9, Pop 20, Push 15, 7 | [9, 20] |
| [15, 7] | 2 | Pop 15, Pop 7 | [15, 7] |
⚠️ Common Pitfalls & Tips
if root == null). Forget this and the while loop might run on a null list or throw errors.Lowest Common Ancestor of a Binary Tree
Level I: Brute Force (Recursive Containment Check)
Intuition
p and q first reside in different subtrees (one in left, one in right), or the node itself is one of p or q. A brute force approach checks for every node if it contains p and q in its subtrees.Thought Process
- Define
contains(node, target)helper. - For current
root, ifrootisporq, it might be LCA. - If
contains(root.left, p & q)is true, move to left child. - If
contains(root.right, p & q)is true, move to right child. - If they are split,
rootis LCA.
Detailed Dry Run
| Node | Left contains p, q? | Right contains p, q? | Decision |
|---|---|---|---|
| 3 | no | no | Split! 3 is LCA |
Level II: Iterative with Parent Map
Intuition
p and q, we can backtrack from p to the root, storing all ancestors in a set. Then, we backtrack from q the first ancestor that exists in that set is the LCA.Logic
- BFS/DFS to build
parentMapuntil bothpandqare found. - Collect all ancestors of
pin a set. - Pull upwards from
quntil an ancestor exists inp's ancestor set.
Detailed Dry Run
Level III: Optimal (Post-order Recursion)
Intuition
Thought Process
p or q?".Logic
- If a subtree returns a non-null node, it means
porqwas found in that subtree. - If both subtrees return non-null, the current node is the Lowest Common Ancestor.
- If only one subtree is non-null, pass that result up.
Pattern
Detailed Dry Run
| Node | Left Result | Right Result | Pass Up |
|---|---|---|---|
| 5 | p | null | p |
| 1 | null | q | q |
| 3 | 5(p) | 1(q) | 3 (LCA) |
⚠️ Common Pitfalls & Tips
p and q exist in the tree. If they might not exist, a double-check pass is required.Binary Tree Zigzag Level Order Traversal
Level I: BFS + Reverse
Intuition
Thought Process
- Standard BFS.
- Maintain
levelIndexstarting at 0. - Collect level nodes.
- If
levelIndex % 2 != 0, reverse level list. - Increment
levelIndex.
Detailed Dry Run
| Level | Nodes | Reverse? | State |
|---|---|---|---|
| 0 | [3] | No | [[3]] |
| 1 | [9, 20] | Yes | [[3], [20, 9]] |
| 2 | [15, 7] | No | [[3], [20, 9], [15, 7]] |
Level II: Two Stacks
Intuition
Logic
- Stack1 (L to R): Push Left child, then Right child to Stack2.
- Stack2 (R to L): Push Right child, then Left child to Stack1.
Detailed Dry Run
Level III: Optimal (BFS + Double-Ended Queue)
Intuition
Thought Process
Reverse step. This is more efficient as we determine the correct spot for each node exactly once.Logic
- Use a standard BFS queue.
- For each level, initialize an empty Deque.
- If
LevelIndexis even: Add to the back (tail). - If
LevelIndexis odd: Add to the front (head).
Complexity
- Time:
- Space: (Queue size)
Detailed Dry Run
| Level | Node | Direction | Deque State |
|---|---|---|---|
| 1 | 9 | Left to Right | [9] |
| 1 | 20 | Left to Right | [9, 20] |
| 2 | 15 | Right to Left | [15] |
| 2 | 7 | Right to Left | [7, 15] |
⚠️ Common Pitfalls & Tips
unshift in JS or appendleft in Python is for Deques, using them on standard arrays can be per operation. Be sure to use appropriate data structures.Path Sum II
Level I: DFS with List Copying
Intuition
Thought Process
- At each node, subtract
node.valfrom the remaining target. - If it's a leaf node and
remainingTarget == 0, add the current path to the result list. - Recurse to left and right children with a fresh copy of the current path.
Detailed Dry Run
| Tree | Target | Path | Remaining | Action |
|---|---|---|---|---|
| 5 | 22 | [5] | 17 | Recurse |
| 4 | 17 | [5, 4] | 13 | Recurse |
| 11 | 13 | [5, 4, 11] | 2 | Recurse (to 2) |
| 2 | 2 | [5, 4, 11, 2] | 0 | SUCCESS! |
Level II: Iterative BFS (Queue with State)
Intuition
(node, currentPath, currentSum) to simulate BFS. For each node, we push its children along with the updated path and sum. This avoids recursion entirely while still exploring the tree level by level.Thought Process
- Store state as
[node, path_list, rem_sum]. - When reaching a leaf, if
rem_sum == node.val, save path. - Push children with
path + [node.val]andrem_sum - node.val.
Detailed Dry Run
Level III: Optimal (Backtracking)
Intuition
Thought Process
Patterns
Complexity
- Time: (Every node visited once)
- Space: (For the recursive stack and the single path list)
Detailed Dry Run
| Action | Path | Sum (Remaining) |
|---|---|---|
| Visit 5 | [5] | 17 |
| Visit 4 | [5, 4] | 13 |
| Visit 11 | [5, 4, 11] | 2 |
| Pop 11 | [5, 4] | 13 |
⚠️ Common Pitfalls & Tips
res.add(new ArrayList<>(path))), otherwise all paths in the result will end up empty at the end of the process!Kth Smallest Element in a BST
Level I: Inorder Traversal to Array
Intuition
k-1.Thought Process
- Traverse the tree (Left, Node, Right).
- Append values to a list.
- Return
list[k-1].
Detailed Dry Run
| Tree | Inorder Array | k | Result |
|---|---|---|---|
| [3,1,4,null,2] | [1, 2, 3, 4] | 1 | 1 |
Level II: Divide and Conquer (using Node counts)
Intuition
Logic
- Let
L = countNodes(root.left). - If
k == L + 1:root.valis the result. - If
k <= L: Search in the left subtree. - If
k > L + 1: Search in the right subtree for the(k - L - 1)-th smallest element.
Detailed Dry Run
Level III: Optimal (Iterative Inorder with Early Exit)
Intuition
Thought Process
Complexity
- Time: where is the height of the tree.
- Space: for the stack.
Detailed Dry Run
| Stack | Current Node | k-count | Action |
|---|---|---|---|
| [3, 1] | null | 0 | Pop 1, Push 2 |
| [3] | 2 | 1 | Pop 1 was 1st smallest. |
| [3] | null | 1 | Pop 2, k reaches 1 (if k=2) |
⚠️ Common Pitfalls & Tips
k == 0 immediately after popping.Populating Next Right Pointers in Each Node
Level I: BFS (Level Order)
Intuition
next pointer of the current node to the next node in the queue (if it's not the last node of the level).Thought Process
- Standard BFS with Queue.
- At each level
i(of sizeS): - For
jfrom 0 toS-1:node.next = queue.peek()(ifj < S-1).
Detailed Dry Run
| Level | Node | Queue After Pop | Next Pointer |
|---|---|---|---|
| 1 | 2 | [3] | 3 |
| 1 | 3 | [] | null |
| 2 | 4 | [5, 6, 7] | 5 |
Level II: Recursive DFS
Intuition
next element, we connect its right child to the next element's left child.Logic
- Base case:
if (!root || !root.left) return. root.left.next = root.right.if (root.next) root.right.next = root.next.left.- Recurse left and right.
Detailed Dry Run
Level III: Optimal (Iterative using Pointers)
Intuition
Thought Process
next pointers from the current level to connect the children of the next level. This avoids the use of a queue, making it space.Logic
- Start at the leftmost node of the current level (
leftmost). - Iterate through nodes at this level using
cur = cur.next. - Connect
cur.left.next = cur.right. - Connect
cur.right.next = cur.next.left(ifcur.nextexists).
Complexity
- Time:
- Space:
Detailed Dry Run
| Level Head | Current | cur.left.next | cur.right.next | Action |
|---|---|---|---|---|
| 1 | 1 | 2->3 | null | Done with L1 |
| 2 | 2 | 4->5 | 5->6 | Move to cur=3 |
| 2 | 3 | 6->7 | null | Done with L2 |
⚠️ Common Pitfalls & Tips
Serialize and Deserialize Binary Tree
Level I: BFS (Level Order)
Intuition
Thought Process
- Use a queue to traverse the tree level by level.
- For each node, append its value to the result string.
- If a child is null, append '#' instead.
- When deserializing, split by ',' and use a queue to reconstruct parent-child links.
Detailed Dry Run
| Tree | Serialized String |
|---|---|
| [1, 2, 3] | "1,2,3,#,#,#,#" |
| [1, null, 2] | "1,#,2,#,#" |
Level II: Postorder DFS
Intuition
Logic
- Serialize:
dfs(left),dfs(right),append(val). - Deserialize: Pop from end of list, read
val, thenroot.right = dfs(),root.left = dfs().
Detailed Dry Run
Level III: Optimal (Preorder DFS)
Intuition
Thought Process
Queue or use an index, then recursively rebuild the structure.Patterns
Complexity
- Time:
- Space:
Detailed Dry Run
| Node | Action | Serialized |
|---|---|---|
| 1 | Visit | [1] |
| 2 | Visit | [1, 2] |
| null | Visit | [1, 2, #] |
| null | Visit | [1, 2, #, #] |
⚠️ Common Pitfalls & Tips
StringBuilder (Java) or join (Python) is much faster than string concatenation.Binary Tree Maximum Path Sum
Level I: Brute Force (Iterate all peak nodes)
Intuition
Logic
- For each node, calculate
max_down(node.left)andmax_down(node.right). - Potential path sum =
node.val + max_down(node.left) + max_down(node.right). - Return the maximum across all nodes.
Detailed Dry Run
Level II: Recursive DFS (Return Pair)
Intuition
Logic
- Return a pair
{max_path_overall, max_gain_to_parent}. max_gain_to_parent = node.val + max(0, left_gain, right_gain).max_path_here = node.val + max(0, left_gain) + max(0, right_gain).max_path_overall = max(max_path_here, left_max_overall, right_max_overall).
Complexity
- Time:
- Space:
Detailed Dry Run
Level III: Optimal (Single-Pass Recursion)
Intuition
Thought Process
- The maximum path sum that passes through this node as the peak (Highest point of an arch).
- The maximum "gain" this node can contribute to a path coming from its parent.
Gain Logic
gain = node.val + max(0, leftGain, rightGain)
We discard negative gains because a path would be shorter if it simply didn't include that subtree.Diagram
-10
/ \
9 20
/ \
15 7
Gain(15) = 15, Gain(7) = 7
At 20: Max path = 15+7+20 = 42. Gain to return to -10 = 20+15 = 35.
At -10: Max path = 9+35-10 = 34.
Result: 42.Detailed Dry Run
| Node | Left Gain | Right Gain | Node Sum | Global Max |
|---|---|---|---|---|
| 15 | 0 | 0 | 15 | 15 |
| 7 | 0 | 0 | 7 | 15 |
| 20 | 15 | 7 | 42 | 42 |
| 9 | 0 | 0 | 9 | 42 |
| -10 | 9 | 35 | 34 | 42 |
⚠️ Common Pitfalls & Tips
max to Integer.MIN_VALUE and use Math.max(..., 0) to decide whether to include subtrees.Recover Binary Search Tree
Level I: Inorder to Array (Identify Swaps)
Intuition
arr[i] > arr[i+1].Thought Process
- Perform inorder traversal to get all values.
- In the sorted values, find the two values that are out of place.
- Traverse the tree again and swap these two values back.
Detailed Dry Run
| Tree Inorder | Swapped Pairs | First Node | Second Node |
|---|---|---|---|
| [1, 3, 2, 4] | (3, 2) | 3 | 2 |
| [3, 2, 1] | (3, 2), (2, 1) | 3 | 1 |
Level II: Iterative Inorder (Stack)
Intuition
prev pointer and identify where prev.val > current.val.Logic
- Use a stack to perform inorder traversal.
- Whenever
prev.val > current.val, identifying the first and last out-of-order nodes. - Swap values of
firstandsecond.
Detailed Dry Run
Level III: Optimal (In-place Inorder)
Intuition
Thought Process
prev node seen. If prev.val > current.val, we have found an anomaly.- Adjacent swaps: Only one anomaly point.
- Non-adjacent swaps: Two anomaly points (first anomaly's
prevand second anomaly'scurrent).
Complexity
- Time:
- Space: (Recursive stack)
Detailed Dry Run
| Prev | Current | Anomaly? | First | Second |
|---|---|---|---|---|
| 3 | 2 | Yes (3>2) | 3 | 2 |
| 2 | 1 | Yes (2>1) | (keep 3) | 1 |
⚠️ Common Pitfalls & Tips
second in every anomaly found. In Case 2 (non-adjacent swaps), second will be updated twice, correctly identifying the further node.Count Univalue Subtrees
Level I: Brute Force (Check each node)
Intuition
n if all nodes in its subtree have the same value as n.val.Thought Process
- Define
isUnival(node, val)helper. - In main
countfunction:- If
isUnival(root, root.val), increment count. - Recurse to children.
- If
Detailed Dry Run
| Node | Subtree Values | All Same? | Action |
|---|---|---|---|
| 1 | [1, 1, 1] | YES | count++ |
| 5 | [5, 5] | YES | count++ |
Level II: Recursive DFS (Parent Passing)
Intuition
Logic
- For each node, check if it's the root of a unival subtree by matching it with its own value using an
isUnival(node, target)helper.
Detailed Dry Run
Level III: Optimal (Bottom-Up DFS)
Intuition
Thought Process
Patterns
Complexity
- Time:
- Space:
Detailed Dry Run
| Node | Left Uni? | Right Uni? | Matches Children? | Count |
|---|---|---|---|---|
| Leaf | true | true | yes | count++ |
| Root | true | true | yes | count++ |
⚠️ Common Pitfalls & Tips
Serialize and Deserialize N-ary Tree
Level I: BFS (Level Order with null markers)
Intuition
null) to distinguish between the children of different parents. During serialization, we use a queue and for every node, we add its children followed by a null to indicate the end of that node's children list.Logic
- Serialize: Queue based BFS. Append
nullafter children of each node. - Deserialize: Use two queues or pointers to rebuild.
Detailed Dry Run
Level II: DFS (with Parentheses)
Intuition
1(3(5,6),2,4). This is a very intuitive human-readable format.Logic
- Serialize:
node.val+(+ serialize(child) for each child +). - Deserialize: Use a stack to track parent nodes and nested parentheses.
Detailed Dry Run
Level III: Optimal (DFS with Child Count)
Intuition
count children for that node.Logic
- Serialization: Append
valandchildren.size(). - Deserialization: Read
val, readsize, then loopsizetimes to build child subtrees.
Detailed Dry Run
| Node | Children | Serialized Array |
|---|---|---|
| 1 | 3 | [1, 3] |
| 3 | 2 | [1, 3, 3, 2] |
| 5 | 0 | [1, 3, 3, 2, 5, 0] |
| 6 | 0 | [1, 3, 3, 2, 5, 0, 6, 0] |
Lowest Common Ancestor III
Level I: Set of Ancestors
Intuition
parent pointer, we can trace the path from node p back to the root, storing all ancestors in a Hash Set. Then, trace back from node q. The first ancestor of q that is already in the set is the LCA.Thought Process
- Traverse from
pto root, adding every node toSet. - Traverse from
qto root. - Return first node found in
Set.
Detailed Dry Run
| Node | Set (Ancestors of P) | In Set? |
|---|---|---|
| P=5 | {5} | - |
| 3 (parent of 5) | {5, 3} | - |
| Q=1 | {5, 3} | NO |
| 3 (parent of 1) | {5, 3} | YES! (Result=3) |
Level II: Depth Alignment
Intuition
p and q. Then, move the deeper node up its parent chain until both nodes are at the same depth. From there, move both up together until they meet.Logic
- Calculate
depthPanddepthQ. - Align depths by moving up parent pointers.
- Move both until
p == q.
Detailed Dry Run
Level III: Optimal (Two Pointers / Intersection)
Intuition
Thought Process
a and b.- Pointer
astarts atp,bstarts atq. - When a pointer reaches the root (
null), it jumps to the other starting node (qorp). - They will eventually meet at the intersection point (the LCA) after at most steps.
Logic
a = (a == null) ? q : a.parent
b = (b == null) ? p : b.parentDetailed Dry Run
| Step | Ptr A | Ptr B | Notes |
|---|---|---|---|
| 1 | 5 | 1 | Start |
| 2 | 3 | 3 | MEET! (LCA=3) |
⚠️ Common Pitfalls & Tips
null jumping logic. If you jump at the wrong node, you might enter an infinite loop. The pointers must switch to the other starting node to align their path lengths.Path Sum IV
Level I: BFS (Iterative path storage)
Intuition
Logic
- Store all input integers in a Map for quick lookup.
- Use a Queue to perform BFS, starting from depth 1, position 1.
- Each entry in the Queue is
(depth, position, currentSum).
Detailed Dry Run
Level II: Recursive DFS (State Tracking)
Intuition
ij, its children are (i+1)(2j-1) and (i+1)(2j). We can track the path sum as we recurse down.Logic
- Store all
numsin a Map. - Start DFS from
11withcurrentSum = 0. - Base case: if key not in Map, return.
Detailed Dry Run
Level III: Optimal (HashMap with Recursive Sum)
Intuition
[Depth, Position, Value]. Depth 1 means root, depth 2 is its children. A position P in depth D has children at position 2P-1 and 2P in depth D+1.Thought Process
- Store the tree in a HashMap:
key = (depth * 10 + position),value = val. - Perform a DFS starting from the root (ID: 11).
- Keep track of the current path sum.
- If a node has no children in the Map, it's a leaf. Add the current path sum to the total result.
Detailed Dry Run
| Input | Map | Depth/Pos | Path Sum | Action |
|---|---|---|---|---|
| 113 | {11: 3} | 1, 1 | 3 | Root |
| 215 | {11: 3, 21: 5} | 2, 1 | 8 | Left Child |
| 221 | {11: 3, 21: 5, 22: 1} | 2, 2 | 4 | Right Child |
Binary Tree Cameras
Level I: BFS (Try all camera placements)
Intuition
Logic
- Generate all combinations of camera placements.
- For each combination, check if all nodes are covered.
- Return the minimum size of a valid combination.
Detailed Dry Run
Level II: Dynamic Programming (Recursive with 3 States)
Intuition
- Node has a camera.
- Node is covered by a child.
- Node is not yet covered (must be covered by parent).
Logic
dp(node, state)returns min cameras for subtree rooted atnodegivenstate. This avoids the greedy assumptions by exploring all valid coverages recursiveley.
Detailed Dry Run
Level III: Optimal (Greedy Post-Order)
Intuition
Thought Process
Node States
- 0: Node is not covered.
- 1: Node has a camera.
- 2: Node is covered but has no camera.
Logic
- If children are not covered (0), parent must have a camera (1).
- If children have a camera (1), parent is covered (2).
- Otherwise, node is not covered (0).
Detailed Dry Run
| Node | Left State | Right State | Node State | Action |
|---|---|---|---|---|
| Leaf | 2 | 2 | 0 | Return 0 (leaf uncovered) |
| Parent | 0 | 0 | 1 | Camera++ (covers leaf) |
| GrandP | 1 | 1 | 2 | Covered by child |
Find Duplicate Subtrees
Level I: Brute Force (Recursive Comparison)
Intuition
Logic
dfs(node)visits every node.- For each node,
isSame(node, otherNode)checks if they are identical. - Very slow ( or depending on implementation).
Detailed Dry Run
Level II: Serialization with Map
Intuition
Logic
- Use Post-order DFS.
- For each node, string =
val + "," + left + "," + right. - Add string to Map.
- If
map.get(string) == 2, add current node to results.
Detailed Dry Run
| Node | Left Serial | Right Serial | Node Serial | Count | Duplicate? |
|---|---|---|---|---|---|
| Leaf 4 | # | # | "4,#,#" | 1 | NO |
| Leaf 4 | # | # | "4,#,#" | 2 | YES! |
| Node 2 | "4,#,#" | # | "2,4,#,#,#" | 1 | NO |
Level III: Optimal (Integer ID Mapping)
Intuition
Thought Process
(node.val, left_child_id, right_child_id) triplet.Logic
- Use a Map
tripletToIDto map triplets of integers to a unique ID. - Use a Map
idCountto track how many times each ID has appeared. - This reduces the problem to because triplet comparison is .
Detailed Dry Run
| Node | Left ID | Right ID | Triplet | New ID | Count | Duplicate? |
|---|---|---|---|---|---|---|
| 4 | 0 | 0 | (4,0,0) | 1 | 1 | NO |
| 4 | 0 | 0 | (4,0,0) | 1 | 2 | YES! |
| 2 | 1 | 0 | (2,1,0) | 2 | 1 | NO |
Vertical Order Traversal
Level I: DFS with List of Nodes per Column
Intuition
col -> List<Node>. After traversal, we sort the keys (cols) and then sort each list of nodes by their depth and value.Logic
- DFS with
(node, col, depth). - Store in
Map<Integer, List<int[]>>(col -> [depth, val]). - Sort.
Detailed Dry Run
Level II: BFS with Sorting
Intuition
Logic
- Queue of
(node, col, depth). - Map of
col -> List of (depth, val). - Sort result lists.
Detailed Dry Run
Level III: Optimal (BFS + Range Tracking)
Intuition
Thought Process
col=0. Left child is col-1, Right is col+1.Logic
- Use BFS with a Queue of
(Node, col). - Store nodes in a Map:
Map<Integer, List<Integer>>(col -> list of vals). - Keep track of
minColandmaxCol. - Loop from
minColtomaxColto collect results.
Detailed Dry Run
| Node | Col | Min | Max | Map |
|---|---|---|---|---|
| 3 | 0 | 0 | 0 | {0: [3]} |
| 9 | -1 | -1 | 0 | {0: [3], -1: [9]} |
| 20 | 1 | -1 | 1 | {0: [3], -1: [9], 1: [20]} |
Construct Binary Tree from Preorder and Postorder
Level I: Recursive with Subarray Search
Intuition
preorder[1]) is the root of the left subtree. We find this element in the postorder array; all elements before it in postorder belong to the left subtree.Thought Process
root = preorder[0].- Left Root =
preorder[1]. - Find index of
preorder[1]inpostorder(linear search). - All elements until that index in
postorderare in the left subtree.
Detailed Dry Run
| Preorder | Postorder | Left Root | Post Index | Left Size |
|---|---|---|---|---|
| [1,2,4,5,3,6,7] | [4,5,2,6,7,3,1] | 2 | 2 | 3 |
Level III: Optimal (Index-based Recursion)
Intuition
Thought Process
Diagram
Pre: [1, 2, 4, 5, 3, 6, 7]
Idx: 0, 1, 2, 3, 4, 5, 6
If we are at 1, next is 2.
Find 2 in Post: Index 2.
Left Subtree count = 2 - 0 + 1 = 3.Detailed Dry Run
| Pre Index | Node Val | Left Val | Post Map Idx | Action |
|---|---|---|---|---|
| 0 | 1 | 2 | 2 | Build 1, proceed to Left |
| 1 | 2 | 4 | 0 | Build 2, proceed to Left |
| 2 | 4 | - | - | Leaf 4, return |
⚠️ Common Pitfalls & Tips
House Robber III
Level I: Brute Force Recursion
Intuition
Logic
rob(root) = max(root.val + rob(left.left) + rob(left.right) + rob(right.left) + rob(right.right), rob(left) + rob(right)).Detailed Dry Run
Level II: Memoization
Intuition
rob(node) in a HashMap to avoid recomputing for the same node multiple times.Detailed Dry Run
Level III: Optimal (Post-order DP Pair)
Intuition
[max if robbed, max if NOT robbed].- If we rob
root, we must NOT rob its children:root.val + left[1] + right[1]. - If we DON'T rob
root, we take the max of robbing or not robbing each child:max(left) + max(right).
Detailed Dry Run
Course4All Technical Board
Verified ExpertSenior Software Engineers & Algorithmic Experts
Our DSA content is authored and reviewed by engineers from top tech firms to ensure optimal time and space complexity analysis.
Found an issue or have a suggestion?
Help us improve! Report bugs or suggest new features on our Telegram group.