Jump into concepts and practice - no test required
or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
▶
Steps
setup
Initialize global pointer 'prev' as null
We start by setting the global pointer 'prev' to null before any recursion begins. This pointer will track the previously processed node in the flattened list.
💡 This initialization is crucial because 'prev' holds the last processed node, allowing us to link the current node's right pointer correctly.
Line:prev = None
💡 Understanding that 'prev' starts as null sets the base case for linking nodes from the bottom up.
traverse
Start recursion at root node 1
The recursion begins at the root node with value 1. The node state changes to active as we start processing it.
💡 Starting at the root is the natural entry point for the recursive flattening process.
Line:def flatten(root: Optional[TreeNode]) -> None:
if not root:
return
flatten(root.right) # STEP 2
💡 The algorithm will first recurse into the right subtree before the left, following the reverse preorder traversal.
traverse
Recurse into right child node 5
From node 1, the recursion moves to its right child node 5, marking it active for processing.
💡 Following the right subtree first ensures the linked list is built from the rightmost node backwards.
Line:flatten(root.right) # STEP 3
💡 The traversal order is right subtree first, which is key to correctly rewiring pointers.
traverse
Recurse into right child node 6
From node 5, the recursion moves to its right child node 6, marking it active for processing.
💡 Continuing right-first traversal reaches the rightmost leaf node, which will be the tail of the flattened list.
Line:flatten(root.right) # STEP 4
💡 The rightmost node is processed first, setting the base for linking nodes backward.
insert
Process node 6: set right to prev (null), left to null, update prev
At node 6, set node.right to prev (currently null), node.left to null, then update prev to node 6.
💡 This step rewires node 6 as the tail of the linked list, starting the chain.
💡 Node 1 now points to node 3, completing the flattened linked list starting at the root.
reconstruct
Traversal complete: flattened linked list formed
The recursion has finished. The tree is now flattened into a linked list starting at node 1, with all left pointers null and right pointers following preorder.
💡 This final step confirms the algorithm's success in transforming the tree structure.
Line:flatten(root) # recursion complete
💡 The flattened list is 1 -> 2 -> 3 -> 4 -> 5 -> 6, matching preorder traversal.
from typing import Optional
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
prev = None # STEP 1
def flatten(root: Optional[TreeNode]) -> None:
global prev
if not root:
return
flatten(root.right) # STEP 2-4
flatten(root.left) # STEP 6,8,9,11
root.right = prev # STEP 5,7,10,12,13
root.left = None # STEP 5,7,10,12,13
prev = root # STEP 5,7,10,12,13
# Driver code
if __name__ == '__main__':
root = TreeNode(1,
TreeNode(2, TreeNode(3), TreeNode(4)),
TreeNode(5, None, TreeNode(6)))
flatten(root) # STEP 14
curr = root
while curr:
print(curr.val, end=' -> ' if curr.right else '')
curr = curr.right
print()
📊
Flatten Binary Tree to Linked List - Watch the Algorithm Execute, Step by Step
Watching this step-by-step visualization reveals how the reverse preorder traversal and pointer rewiring work together to flatten the tree efficiently, which is difficult to grasp from code alone.
Step 1/14
·Active fill★Answer cell
123456
Current node: 1
123456
DFS Stack
1 (entered)
Current node: 5
123456
DFS Stack
5 (entered)1 (right_done)
Current node: 6
123456
DFS Stack
6 (entered)5 (right_done)1 (right_done)
Current node: 6
123456
DFS Stack
5 (right_done)1 (right_done)
123456
DFS Stack
5 (left_done)1 (right_done)
Current node: 5
123456
DFS Stack
1 (right_done)
Current node: 2
123456
DFS Stack
2 (entered)1 (left_done)
Current node: 4
123456
DFS Stack
2 (right_done)1 (left_done)
Current node: 4
123456
DFS Stack
2 (right_done)1 (left_done)
Current node: 3
123456
DFS Stack
3 (entered)2 (left_done)1 (left_done)
Current node: 3
123456
DFS Stack
1 (left_done)
Current node: 1
123456
123456
Result: [1, 2, 3, 4, 5, 6]
Key Takeaways
✓ The reverse preorder traversal (right-left-root) is essential to flatten the tree from the bottom up.
This traversal order is counterintuitive but necessary to link nodes correctly without losing references.
✓ The global 'prev' pointer tracks the last processed node, enabling each node to link to the next node in the flattened list.
Understanding 'prev' is key to grasping how the linked list is constructed incrementally.
✓ Setting each node's left pointer to null and right pointer to 'prev' rewires the tree into a singly linked list in-place.
This pointer manipulation is the core transformation that converts the tree structure.
Practice
(1/5)
1. Consider the following Python code implementing the Morris Preorder Traversal approach to sum root-to-leaf numbers. Given the binary tree:
1
/ \
2 3
What is the final value of the variable total returned by sumNumbers?
easy
A. 5
B. 15
C. 25
D. 26
Solution
Step 1: Trace path 1->2
current_number accumulates 1 then 12; leaf node 2 adds 12 to total.
Step 2: Trace path 1->3
current_number resets to 1, then accumulates 13; leaf node 3 adds 13 to total. Total = 12 + 13 = 25.
Step 3: Check for off-by-one or missed increments
Integer division after visiting left subtree correctly adjusts current_number; no extra addition occurs.
Final Answer:
Option C -> Option C
Quick Check:
Sum of 12 and 13 is 25, matching code behavior [OK]
Hint: Trace current_number updates carefully at each node [OK]
Common Mistakes:
Off-by-one in current_number division
Adding non-leaf nodes to total
2. Given the following code snippet for checking if a binary tree is symmetric, what is the final return value when called with the tree root = TreeNode(1, TreeNode(2), TreeNode(2))?
from typing import Optional
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def isSymmetric(root: Optional[TreeNode]) -> bool:
def isMirror(t1: Optional[TreeNode], t2: Optional[TreeNode]) -> bool:
if not t1 or not t2:
return t1 == t2
if t1.val != t2.val:
return False
return isMirror(t1.left, t2.right) and isMirror(t1.right, t2.left)
return isMirror(root.left, root.right) if root else True
root = TreeNode(1, TreeNode(2), TreeNode(2))
print(isSymmetric(root)) # What is the output?
easy
A. false
B. Raises an exception
C. null
D. true
Solution
Step 1: Trace isMirror on root.left and root.right nodes with value 2
Both nodes exist and have equal values, so recursion continues.
Step 2: Recursively check left.left vs right.right and left.right vs right.left (both null)
Since both pairs are null, isMirror returns true for these calls, so overall returns true.
Final Answer:
Option D -> Option D
Quick Check:
Symmetric tree with equal child nodes returns true [OK]
Hint: Symmetric tree with equal children returns true [OK]
Common Mistakes:
Confusing null checks causing exceptions
Returning false prematurely on equal nodes
Misreading recursion base cases
3. What is the time complexity of the BFS-based serialization and deserialization of a binary tree with n nodes, assuming the tree is balanced? Consider both serialization and deserialization separately.
medium
A. Serialization: O(n log n), Deserialization: O(n log n)
B. Serialization: O(n), Deserialization: O(n^2)
C. Serialization: O(n), Deserialization: O(n)
D. Serialization: O(n^2), Deserialization: O(n)
Solution
Step 1: Analyze serialization time
Each node is enqueued and dequeued exactly once, so serialization is O(n).
Step 2: Analyze deserialization time
Deserialization processes each node once, reconstructing the tree in O(n) time.
Final Answer:
Option C -> Option C
Quick Check:
Both serialization and deserialization scale linearly with number of nodes [OK]
Hint: Each node is processed once in BFS serialization/deserialization [OK]
Common Mistakes:
Assuming deserialization is O(n^2) due to nested loops
Confusing tree height with number of nodes
Thinking serialization is O(n log n) due to queue operations
4. Suppose the problem is modified so that the tree can have nodes with arbitrary large depth, and you want to avoid recursion stack overflow. Which approach is best to check if the tree is balanced efficiently?
hard
A. Use the brute force recursive height check since it is simpler to implement.
B. Use iterative postorder traversal with a stack to compute heights and check balance.
C. Use a breadth-first traversal and check balance at each level.
D. Use a global variable to store height and recurse with tail recursion optimization.
Solution
Step 1: Understand recursion stack limitations
Deep trees can cause recursion stack overflow in recursive solutions.
Step 2: Identify iterative approach benefits
Iterative postorder traversal uses explicit stack, avoiding recursion limits and still computes heights and balance in O(n) time.
Assuming recursion with tail call optimization works in Python
Using BFS which does not check subtree height balance
Choosing brute force recursion despite stack limits
5. Suppose you want to perform inorder traversal on a binary tree where nodes can have parent pointers but no left or right child pointers. Which approach correctly produces the inorder sequence without recursion or extra stack?
hard
A. Use Morris traversal by creating threads on parent pointers instead of child pointers.
B. Use breadth-first search since parent pointers allow level order traversal.
C. Use recursive traversal ignoring parent pointers, which will fail due to missing child links.
D. Use iterative traversal with a pointer to the current node and track previously visited node to decide traversal direction.
Solution
Step 1: Understand traversal constraints
Without left/right child pointers, Morris traversal is not applicable since it relies on child links.
Step 2: Use parent pointers to simulate traversal
By tracking current and previously visited nodes, we can move up or down the tree to simulate inorder traversal iteratively.
Final Answer:
Option D -> Option D
Quick Check:
Tracking previous node enables correct traversal without extra space [OK]