142. Flatten Binary Tree to Linked List
๐ LeetCode Problem: 114. Flatten Binary Tree to Linked List
๐ Difficulty: Medium
๐ท๏ธ Topics: Binary Trees, DFS, Preorder, In-place, Linked List, Post-order
Problem Statement
Given the root of a binary tree, flatten the tree into a "linked list":
- The "linked list" should use the same
TreeNodeclass where therightchild pointer points to the next node in the list and theleftchild pointer is alwaysnull. - The "linked list" should be in the same order as a preorder traversal of the binary tree.
Example 1:
Input: root = [1,2,5,3,4,null,6]
Output: [1,null,2,null,3,null,4,null,5,null,6]
Example 2:
Input: root = []
Output: []
Example 3:
Input: root = [0]
Output: [0]
Follow up: Can you flatten the tree in-place (with O(1) extra space)?
Constraints:
- The number of nodes in the tree is in the range [0, 2000]
- -100 <= Node.val <= 100
๐ณ Visual Understanding - The Complete Transformation!
What Does "Flatten" Mean?
ORIGINAL TREE:
1
/ \
2 5
/ \ \
3 4 6
FLATTENED "LINKED LIST":
1
\
2
\
3
\
4
\
5
\
6
Key observations:
โ All nodes connected via RIGHT pointers
โ All LEFT pointers become null
โ Order follows PREORDER traversal (1,2,3,4,5,6)
โ Same TreeNode objects, just relinked!
โ In-place transformation (no new nodes created)
Why Preorder?
Preorder = Root โ Left subtree โ Right subtree
Example tree: 1
/ \
2 5
/ \ \
3 4 6
Preorder visits:
1 (root) โ 2,3,4 (entire left) โ 5,6 (entire right)
= [1, 2, 3, 4, 5, 6]
This is EXACTLY the flattened order we need! โ
Step-by-Step Visual Transformation:
STEP 0: Original
1
/ \
2 5
/ \ \
3 4 6
STEP 1: Flatten left subtree (2)
1 2 (flattened)
/ \ \
? 5 3
\
4
STEP 2: Flatten right subtree (5)
1 5 (flattened)
/ \ \
? ? 6
STEP 3: Connect at root (1)
- Move flattened left (2โ3โ4) to right of 1
- Attach flattened right (5โ6) to end
1
\
2
\
3
\
4
\
5
\
6
DONE! Perfect chain! โจ
๐ง The AHA Moment - Post-order Recursion!
The Key Insight:
To flatten a tree:
1. Flatten LEFT subtree (recursively)
2. Flatten RIGHT subtree (recursively)
3. Connect them at root:
root โ flattened_left โ flattened_right
Why POST-ORDER?
We need children FLATTENED before we connect them!
If we try to connect before flattening:
Children aren't in the right form yet! โ
Post-order processes children FIRST:
Children are already flattened! โ
Visual Example:
Tree:
1
/ \
2 3
Post-order recursion:
1. flatten(2) โ Already a leaf, done!
Result: 2 (no changes)
2. flatten(3) โ Already a leaf, done!
Result: 3 (no changes)
3. flatten(1) โ Now connect:
- Save right: temp = 3
- Move left to right: 1.right = 2
- Clear left: 1.left = null
- Find end of 2: it's 2 itself
- Attach temp: 2.right = 3
Result: 1โ2โ3 โ
Children flattened BEFORE parent connects them!
๐ง Recursive Thinking - Building the Solution
Base Case:
What's the simplest tree to flatten?
Case 1: null โ Nothing to do, return
Case 2: Leaf node โ Already flat! No children to connect
if (root == null) return;
For leaves, the recursion handles them naturally:
flatten(null) โ returns immediately
No left or right โ nothing to connect
Already in "flattened" form!
Recursive Case:
For any node with children:
STEP 1: Flatten children (POST-ORDER!)
flatten(root.left) // Left subtree now flattened
flatten(root.right) // Right subtree now flattened
STEP 2: Save right subtree (will be overwritten!)
TreeNode tempRight = root.right
STEP 3: Move flattened left to right
root.right = root.left
root.left = null
STEP 4: Find end of moved left subtree
TreeNode current = root
while (current.right != null):
current = current.right
STEP 5: Attach saved right subtree
current.right = tempRight
Result: root โ left chain โ right chain โ
๐ฏ Solution 1: Recursive Post-order (Clearest!)
Implementation:
class Solution {
public void flatten(TreeNode root) {
// Base case: null tree
if (root == null) {
return;
}
// POST-ORDER: Flatten children FIRST
// WHY? Need subtrees flattened before connecting
flatten(root.left);
flatten(root.right);
// Now both children are flattened chains
// Time to connect at current node
// STEP 1: Save right subtree
// WHY? About to overwrite root.right with left!
TreeNode tempRight = root.right;
// STEP 2: Move left chain to right
// WHY? Preorder visits left before right
root.right = root.left;
root.left = null; // Clear left pointer
// STEP 3: Find end of moved left chain
// WHY? Need to attach tempRight at the END
TreeNode current = root;
while (current.right != null) {
current = current.right;
}
// STEP 4: Attach saved right chain
current.right = tempRight;
// Done! root โ left chain โ right chain โ
}
}
Why Each Step Matters:
If we skip saving tempRight:
root.right = root.left // Lost reference to right!
// Can never attach right subtree โ
If we don't find the end:
current.right = tempRight // Attaches at wrong place!
// Loses part of left subtree โ
If we don't clear left:
root.left still has value
// Not a true "linked list" โ
Every step is CRITICAL! ๐ฏ
๐ Detailed Dry Run - Complete Recursion Visualization
Tree:
1
/ \
2 5
/ \ \
3 4 6
Complete Call Stack with Tree States:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
CALL STACK VISUALIZATION
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Level 0: flatten(1)
โ
โโ Tree state at start:
โ 1
โ / \
โ 2 5
โ / \ \
โ 3 4 6
โ
โโ LEFT: flatten(2) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ Level 1: flatten(2) โ
โ โ โ
โ โโ Tree state: โ
โ โ 2 โ
โ โ / \ โ
โ โ 3 4 โ
โ โ โ
โ โโ LEFT: flatten(3) โโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ โ
โ โ Level 2: flatten(3) โ โ
โ โ โ โ โ
โ โ โโ Tree state: 3 (leaf) โ โ
โ โ โโ LEFT: flatten(null) โ return โ โ
โ โ โโ RIGHT: flatten(null) โ return โ โ
โ โ โโ No children, nothing to connect โ โ
โ โ โโ return โ โ
โ โ โ โ
โ โโ RIGHT: flatten(4) โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ โ
โ โ Level 2: flatten(4) โ โ
โ โ โ โ โ
โ โ โโ Tree state: 4 (leaf) โ โ
โ โ โโ LEFT: flatten(null) โ return โ โ
โ โ โโ RIGHT: flatten(null) โ return โ โ
โ โ โโ No children, nothing to connect โ โ
โ โ โโ return โ โ
โ โ โ โ
โ โโ Now connect at node 2: โ โ
โ โ โ โ
โ โ BEFORE connection: โ โ
โ โ 2 โ โ
โ โ / \ โ โ
โ โ 3 4 โ โ
โ โ โ โ
โ โ STEP 1: tempRight = 4 โ โ
โ โ STEP 2: 2.right = 3, 2.left = null โ โ
โ โ STEP 3: Find end (current = 2 โ 3) โ โ
โ โ STEP 4: 3.right = 4 โ โ
โ โ โ โ
โ โ AFTER connection: โ โ
โ โ 2 โ โ
โ โ \ โ โ
โ โ 3 โ โ
โ โ \ โ โ
โ โ 4 โ โ
โ โ โ โ
โ โโ return โ โ
โ โ
โโ RIGHT: flatten(5) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ Level 1: flatten(5) โ
โ โ โ
โ โโ Tree state: โ
โ โ 5 โ
โ โ \ โ
โ โ 6 โ
โ โ โ
โ โโ LEFT: flatten(null) โ return โ
โ โ โ
โ โโ RIGHT: flatten(6) โโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ โ
โ โ Level 2: flatten(6) โ โ
โ โ โ โ โ
โ โ โโ Tree state: 6 (leaf) โ โ
โ โ โโ LEFT: flatten(null) โ return โ โ
โ โ โโ RIGHT: flatten(null) โ return โ โ
โ โ โโ return โ โ
โ โ โ โ
โ โโ Now connect at node 5: โ โ
โ โ โ โ
โ โ BEFORE: โ โ
โ โ 5 โ โ
โ โ \ โ โ
โ โ 6 โ โ
โ โ โ โ
โ โ STEP 1: tempRight = 6 โ โ
โ โ STEP 2: 5.right = null, 5.left = null โ โ
โ โ STEP 3: Find end (current = 5, stop) โ โ
โ โ STEP 4: 5.right = 6 โ โ
โ โ โ โ
โ โ AFTER: โ โ
โ โ 5 โ โ
โ โ \ โ โ
โ โ 6 โ โ
โ โ (No change, already flattened!) โ โ
โ โ โ โ
โ โโ return โ โ
โ โ
โโ Now connect at node 1: โ
โ โ
โ BEFORE connection: โ
โ 1 โ
โ / \ โ
โ 2 5 (where 2โ3โ4 and 5โ6) โ
โ \ \ โ
โ 3 6 โ
โ \ โ
โ 4 โ
โ โ
โ STEP 1: tempRight = 5 (points to 5โ6) โ
โ STEP 2: 1.right = 2 (points to 2โ3โ4) โ
โ 1.left = null โ
โ STEP 3: Find end of moved chain โ
โ current = 1 โ 2 โ 3 โ 4 (stop!) โ
โ STEP 4: 4.right = 5 (attach 5โ6) โ
โ โ
โ AFTER connection: โ
โ 1 โ
โ \ โ
โ 2 โ
โ \ โ
โ 3 โ
โ \ โ
โ 4 โ
โ \ โ
โ 5 โ
โ \ โ
โ 6 โ
โ โ
โโ DONE! Perfect chain! โจ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
FINAL RESULT: 1โ2โ3โ4โ5โ6 (all via right pointers)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ฏ Solution 2: Iterative Morris-like (O(1) Space!) - DETAILED
The Challenge:
Can we flatten WITHOUT recursion?
โ No call stack = O(1) space!
Key insight:
At each node with left subtree, we need to:
1. Insert left subtree between current and right
2. Do this WITHOUT recursion!
How?
Process nodes left-to-right, top-to-bottom
At each node: handle its left subtree completely
Then move right (which now includes moved left)
The Morris-like Strategy Explained:
Core idea:
For node with left child:
BEFORE:
current
/ \
left right
WANT:
current
\
left
\
(end of left subtree)
\
right
How?
1. Find END of left subtree (rightmost node)
2. Connect end to right subtree
3. Move left to right position
4. Clear left pointer
5. Continue right (now processes moved left)
Visual Example - Step by Step:
INITIAL TREE:
1
/ \
2 5
/ \ \
3 4 6
Goal: 1โ2โ3โ4โ5โ6
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
STEP-BY-STEP TRANSFORMATION
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
STEP 1: Process node 1
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Current tree:
1
/ \
2 5
/ \ \
3 4 6
1.a) current = 1, left exists (2)
1.b) Find rightmost of left subtree:
Start at 2
2
/ \
3 4
2.right exists? YES (4)
Move to 4
4.right exists? NO
STOP at 4 โ
rightmost = 4
1.c) Connect rightmost to current.right:
4.right = 5
Now 4 points to 5:
2
/ \
3 4 โ 5
\
6
1.d) Move left to right:
1.right = 2
1.e) Clear left:
1.left = null
Result after step 1:
1
\
2
/ \
3 4 โ 5
\
6
Note: Node 4 is connected to 5!
This preserves the original right subtree!
Move to next: current = 1.right = 2
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
STEP 2: Process node 2
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Current tree:
1
\
2
/ \
3 4 โ 5
\
6
2.a) current = 2, left exists (3)
2.b) Find rightmost of left subtree:
Start at 3
3.right exists? NO
STOP at 3 โ
rightmost = 3
2.c) Connect rightmost to current.right:
3.right = 4 (which points to 5)
Now: 3 โ 4 โ 5 โ 6
2.d) Move left to right:
2.right = 3
2.e) Clear left:
2.left = null
Result after step 2:
1
\
2
\
3
\
4 โ 5
\
6
Move to next: current = 2.right = 3
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
STEP 3: Process node 3
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Current tree:
1
\
2
\
3
\
4 โ 5
\
6
3.a) current = 3, left exists? NO
3.b) No left child, nothing to process
Move to next: current = 3.right = 4
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
STEP 4: Process node 4
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Current tree:
1
\
2
\
3
\
4
\
5
\
6
4.a) current = 4, left exists? NO
4.b) No left child, nothing to process
Move to next: current = 4.right = 5
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
STEP 5: Process node 5
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
5.a) current = 5, left exists? NO
5.b) No left child, nothing to process
Move to next: current = 5.right = 6
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
STEP 6: Process node 6
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
6.a) current = 6, left exists? NO
6.b) No left child, nothing to process
Move to next: current = 6.right = null
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
DONE! current = null
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
FINAL FLATTENED TREE:
1
\
2
\
3
\
4
\
5
\
6
SUCCESS! โ
Implementation with Detailed Comments:
class Solution {
public void flatten(TreeNode root) {
TreeNode current = root;
// Process nodes from left to right
while (current != null) {
// Only process if left child exists
if (current.left != null) {
// STEP 1: Find rightmost node of left subtree
// This is the END of the left subtree chain
TreeNode rightmost = current.left;
while (rightmost.right != null) {
rightmost = rightmost.right;
}
// At this point, rightmost.right == null
// rightmost is the LAST node in left subtree
// STEP 2: Connect rightmost to current's right
// This PRESERVES the original right subtree
// It will be attached after the entire left subtree
rightmost.right = current.right;
// STEP 3: Move left subtree to right
// Now current.right points to the left subtree
current.right = current.left;
// STEP 4: Clear left pointer
// After moving, left should be null (linked list property)
current.left = null;
// After these steps:
// - Left subtree is now on the right
// - End of left subtree connects to original right
// - Left pointer is null
}
// STEP 5: Move to next node
// This will process the moved left subtree next
current = current.right;
}
}
}
Why This Algorithm Works:
KEY INSIGHT: Rightmost node connection!
When we find rightmost of left subtree,
we connect it to the current right subtree.
This ensures: left โ ... โ rightmost โ right
Example:
current (1)
/ \
2 5
/ \ \
3 4 6
Rightmost of left = 4
Connect: 4 โ 5
Then move left to right: 1 โ 2
Result: 1 โ 2 โ 3 โ 4 โ 5 โ 6
The rightmost connection is KEY! ๐
Why O(1) Space?
Space used:
- current pointer: O(1)
- rightmost pointer: O(1)
- NO recursion stack: O(0)
- NO extra data structures: O(0)
Total: O(1) extra space! โ
Time: O(n) - each node visited at most twice
1. As current
2. When finding rightmost
Still efficient! ๐
Common Mistakes - Morris Approach:
โ MISTAKE 1: Not finding true rightmost
rightmost = current.left;
// Forgot to traverse right!
This only gets immediate left child,
not the END of left subtree! โ
โ CORRECT:
while (rightmost.right != null) {
rightmost = rightmost.right;
}
โ MISTAKE 2: Not connecting rightmost
rightmost = current.left;
current.right = current.left;
current.left = null;
// Lost the original right subtree! โ
โ CORRECT:
rightmost.right = current.right; // Save it first!
current.right = current.left;
โ MISTAKE 3: Clearing left before moving
current.left = null;
current.right = current.left; // Now null! โ
โ CORRECT: Move first, clear second
current.right = current.left;
current.left = null;
Visual Comparison - Recursive vs Iterative:
RECURSIVE (Post-order):
โ Cleaner, more intuitive
โ Natural top-down flow
โ O(h) space (call stack)
โ Can't optimize space
ITERATIVE (Morris-like):
โ O(1) space! No recursion!
โ Same time complexity
โ More complex logic
โ Harder to understand initially
Both correct! Choose based on constraints:
- Space critical? โ Iterative
- Clarity critical? โ Recursive
๐ Complexity Analysis
Recursive Solution:
Time Complexity: O(nยฒ) worst case, O(n log n) average
Visit each node: O(n)
At each node:
- Flatten children: covered by "visit each"
- Find end of moved left: O(k) where k = left subtree size
Worst case (left-skewed tree):
1
/
2
/
3
/
4
At 1: find end through all left nodes (O(n))
At 2: find end (O(n-1))
At 3: find end (O(n-2))
...
Total: O(n) + O(n-1) + ... + O(1) = O(nยฒ)
Average case (balanced): O(n log n)
Each level: O(n/2^i) nodes ร O(i) to find end
Sum over levels: O(n log n)
Space Complexity: O(h)
Recursion call stack: O(h) where h = height
Best case (balanced): O(log n)
Worst case (skewed): O(n)
Iterative Solution:
Time Complexity: O(n)
Each node visited at most twice:
1. Once as current
2. Once when finding rightmost
But wait - finding rightmost can traverse multiple nodes!
Actually: Each node visited exactly ONCE
- Either as current OR as part of rightmost search
- But never both (once processed, moved to right)
Total: O(n) โ
This is BETTER than recursive! ๐ก
Space Complexity: O(1)
Only use a few pointers:
- current
- rightmost
No recursion stack!
True O(1) space! โ
This satisfies the follow-up! ๐ฏ
โ ๏ธ Common Mistakes
Mistake 1: Not Saving Right Subtree
// โ WRONG - Lost right subtree!
flatten(root.left);
flatten(root.right);
root.right = root.left; // Overwrites root.right!
root.left = null;
// Now we have no reference to original right subtree!
// It's LOST! โ
// โ CORRECT - Save first!
TreeNode tempRight = root.right; // Save it!
root.right = root.left;
root.left = null;
// Now we can attach tempRight later
current.right = tempRight; โ
Mistake 2: Not Finding End of Moved Subtree
// โ WRONG - Attaches at wrong place!
root.right = root.left;
root.left = null;
root.right.right = tempRight; // WRONG! root.right might have children!
// Example tree: 1
// /
// 2
// /
// 3
//
// Would create: 1
// \
// 2 โ tempRight (lost 3!) โ
// /
// 3
// โ CORRECT - Find the actual END
TreeNode current = root;
while (current.right != null) {
current = current.right;
}
current.right = tempRight; // Attach at END โ
Mistake 3: Forgetting to Set Left to Null
// โ WRONG - Left pointers remain!
root.right = root.left;
// Forgot: root.left = null;
flatten(root.right);
// Result: 1
// / \ โ Both pointers exist!
// 2 2 (same node!)
//
// Not a "linked list"! โ
// โ CORRECT - Always clear left!
root.right = root.left;
root.left = null; // Critical! โ
Mistake 4: Wrong Recursion Order (Preorder)
// โ WRONG - Preorder recursion
// (Modifies tree BEFORE processing children)
TreeNode tempRight = root.right;
root.right = root.left;
root.left = null;
flatten(root.left); // Now null! Lost reference! โ
flatten(root.right);
// We modified tree before recursing!
// Lost access to original children! โ
// โ CORRECT - Post-order
flatten(root.left); // Flatten children FIRST
flatten(root.right);
// NOW reconnect (children already processed)
TreeNode tempRight = root.right;
root.right = root.left;
root.left = null;
// ... โ
Mistake 5: Iterative - Moving Right Before Clearing Left
// โ WRONG - Creates cycle!
while (current != null) {
if (current.left != null) {
rightmost.right = current.right;
current.right = current.left;
// Forgot: current.left = null;
current = current.right; // Now current and current.left both point to same node!
}
}
// Creates circular reference! โ
// โ CORRECT - Clear left before moving
current.left = null; // Clear first!
current = current.right; โ
๐ฏ Pattern Recognition - In-Place Tree Transformation
Core Pattern: Post-order Tree Restructuring
Template for in-place transformations:
void transform(TreeNode node) {
if (node == null) return;
// POST-ORDER: Transform children first
// WHY? Need them in final form before connecting
TreeNode savedLeft = node.left;
TreeNode savedRight = node.right;
transform(savedLeft);
transform(savedRight);
// Now reconnect with new structure
reconnect(node, savedLeft, savedRight);
}
Related Problems:
1. Invert Binary Tree (Problem 136)
In-place transformation
Swaps left and right
Simpler: just swap pointers!
2. Convert BST to Sorted Doubly Linked List
In-place restructuring
Uses inorder traversal (sorted)
Connect nodes with left/right as prev/next
Similar connection logic!
3. Binary Tree to Circular Doubly Linked List
In-place + circular connection
Track first and last nodes
Connect tail to head at end
4. Serialize and Deserialize Binary Tree
Not in-place, but related
Uses preorder for serialization
Same order as flatten! โ
When to Use This Pattern:
โ "In-place" or "O(1) space" in problem
โ Restructure tree keeping same nodes
โ Change pointer relationships
โ Transform to list/different structure
โ Preserve certain traversal order
โ No new nodes created
๐ Quick Revision Notes
๐ฏ Core Concept
Flatten tree to right-skewed "linked list" following preorder order. All nodes connected via right pointers, left = null. Two approaches: (1) Recursive post-order (flatten children, then connect - clearest logic), (2) Iterative Morris-like (O(1) space! - at each node, find rightmost of left, connect to right, move left to right). Key insight: Process children FIRST (post-order), then reconnect at parent.
โก Quick Implementation
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
public static TreeNode buildTree(Integer[] arr) {
if (arr == null || arr.length == 0 || arr[0] == null) {
return null;
}
TreeNode root = new TreeNode(arr[0]);
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int i = 1;
while (!queue.isEmpty() && i < arr.length) {
TreeNode current = queue.poll();
// Process left child
if (i < arr.length) {
if (arr[i] != null) {
current.left = new TreeNode(arr[i]);
queue.offer(current.left);
}
i++;
}
// Process right child
if (i < arr.length) {
if (arr[i] != null) {
current.right = new TreeNode(arr[i]);
queue.offer(current.right);
}
i++;
}
}
return root;
}
public static void print(TreeNode root) {
if (root == null) {
System.out.println("[]");
return;
}
List<String> result = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
if (node != null) {
result.add(String.valueOf(node.val));
queue.offer(node.left);
queue.offer(node.right);
} else {
result.add("null");
}
}
while (!result.isEmpty() && result.get(result.size() - 1).equals("null")) {
result.remove(result.size() - 1);
}
System.out.println(result);
}
}
class Solution {
public void flatten(TreeNode root) {
// // Approach 1 - straight forward - check visual understanding section.
// usingPostOrder(root);
// Approach 2 - Morris
usingMorris(root);
}
private void usingMorris(TreeNode root) {
// Steps - check example in LC to understand these steps
// 1. Find right most node of the left subtree (of root).
// 2. Attach the right subtree (of root) to the above right most end of the left subtree.
// 3. Now, move to next node which is right of root (root.right). No more left exists at root now.
// 4. Repeat steps 1,2,3 for every root (and later root = root.right) only when root.left exists
while (root != null) {
// Above steps 1,2,3 in case only left exists. Else, proceed to right
if(root.left != null) {
// Step 1 - finding the right most node of the left subtree.
TreeNode curr = root.left;
while (curr.right != null) {
curr = curr.right;
}
// Step 2
// attach the current right subtree to above curr.
// move current left subtree to root right
// nullify root left as its moved to right.
curr.right = root.right;
root.right = root.left;
root.left = null;
// Step 3 - move to next node. Still my root is at 1.
root = root.right;
} else {
root = root.right;
}
}
}
private void usingPostOrder(TreeNode root) {
// base case - return in case of leaf node
if(root == null) {
return;
}
// Steps
// 1. flatten left
// 2. flatten right
// 3. connect left to root and right to left (handle disconnects while connecting)
usingPostOrder(root.left); // step 1
usingPostOrder(root.right); // step 2
TreeNode temp = root.right; // preserve right to connect to end of left subtree
// connecting left subtree to root right and nullify root left.
root.right = root.left;
root.left = null;
// since left subtree is flattened, traverse towards the end of flattened subtree, which
// is now a linked list. At the end of it, attach previously saved right.
TreeNode curr = root;
while (curr != null && curr.right != null) {
curr = curr.right; // no need to bother about left as all child subtrees would be flattened by now and no left children
}
curr.right = temp; // attached right flattened sub tree to end of the left flattened sub tree
}
public static void main(String[] args) {
Solution s = new Solution();
TreeNode root1 = TreeNode.buildTree(new Integer[] {1,2,5,3,4,null,6});
s.flatten(root1);
TreeNode.print(root1);
}
}
๐ Key Insights
Why Post-Order?
Need children FLATTENED before connecting!
Preorder would modify tree during recursion:
โ Lose references to children
โ Can't recurse properly
Post-order processes children FIRST:
โ Children already in final form
โ Safe to reconnect at parent
Children first, parent last = POST-ORDER! ๐ฏ
The Reconnection Dance:
At each node (after children flattened):
1. Save right โ tempRight
WHY? Will be overwritten
2. Move left to right
WHY? Preorder visits left first
3. Set left = null
WHY? "Linked list" has no lefts
4. Find end of moved left
WHY? Need to attach tempRight there
5. Attach tempRight
WHY? Complete the chain!
All 5 steps required! ๐
O(1) Space Achievement:
Recursive: O(h) space (call stack)
Iterative: O(1) space! โ
How?
- No recursion (no call stack)
- Only few pointers (current, rightmost)
- Process left-to-right, top-to-bottom
This satisfies the follow-up! ๐ฏ
๐ช Memory Aid
"Post-order: children first, then connect!"
"Save before overwrite!"
"Find end, then attach!"
"Iterative = O(1) space magic!" โจ
โ ๏ธ Don't Forget
- Post-order recursion (children before parent!)
- Save right subtree (before overwriting!)
- Set left = null (all lefts must be null!)
- Find END (not just root.right!)
- Iterative for O(1) (no recursion stack!)
- Preorder defines order (1โ2โ3โ4โ5โ6!)
๐ Congratulations!
You've mastered in-place tree transformation!
What You Learned:
โ
In-place restructuring - Modify without new nodes
โ
Post-order processing - Children before parent
โ
Pointer manipulation - Save, move, find, connect
โ
Preorder application - Order matches flatten
โ
O(1) space solution - Iterative Morris-like approach
โ
Complex tree transformation - Multi-step reconnection
The Complete Picture:
Problem 136: Invert
- Simple swap
- Can use any order
Problem 142: Flatten
- Complex restructuring
- MUST use post-order
- Preorder result order
- O(1) space possible
Advanced transformation mastery! ๐
Interview Mastery:
Interviewer: "Flatten tree in-place"
Your approach:
1. "Preorder traversal order" โ
2. "Post-order recursion to process" โ
3. "Save-move-find-attach pattern" โ
4. "Can optimize to O(1) space" โ
Codes recursive in 8 minutes โ
Explains iterative optimization โ
Handles all edge cases โ
Perfect response! ๐ฏ
You're a tree transformation expert! ๐ณ๐งโจ