226. Invert Binary Tree
226. Invert Binary Tree
Problem Statement
Given the root of a binary tree, invert the tree, and return its root.
Examples
Example 1:
Input: root = [4,2,7,1,3,6,9]
Output: [4,7,2,9,6,3,1]
Before inversion:
4
/ \
2 7
/ \ / \
1 3 6 9
After inversion:
4
/ \
7 2
/ \ / \
9 6 3 1
Example 2:
Input: root = [2,1,3]
Output: [2,3,1]
Before inversion:
2
/ \
1 3
After inversion:
2
/ \
3 1
Example 3:
Input: root = []
Output: []
Constraints
- The number of nodes in the tree is in the range
[0, 100]. -100 <= Node.val <= 100
Clarification Questions
Before diving into the solution, here are 5 important clarifications and assumptions to discuss during an interview:
-
Inversion definition: What exactly does “invert” mean? (Assumption: Swap left and right children for every node in the tree)
-
Tree modification: Should we modify the tree in-place or return a new tree? (Assumption: Modify in-place - return the root of the modified tree)
-
Empty tree: What should we return for an empty tree? (Assumption: Return null - no tree to invert)
-
Single node: What happens with a tree containing only root? (Assumption: Return root unchanged - no children to swap)
-
Recursive vs iterative: Are there any constraints on using recursion? (Assumption: No - recursion is fine, but iterative approach also works)
Interview Deduction Process (10 minutes)
Step 1: Brute-Force Approach (2 minutes)
Create a new tree with the same structure but swapped children. Traverse the original tree, and for each node, create a new node with left and right children swapped. This approach works but requires O(n) extra space for the new tree and O(n) time to build it. Since we’re modifying in-place, this isn’t necessary.
Step 2: Semi-Optimized Approach (3 minutes)
Use iterative BFS with a queue. Process nodes level by level, swapping left and right children for each node, then enqueue both children. This approach uses O(w) space where w is the maximum width of the tree, and O(n) time. It works well but requires maintaining a queue data structure.
Step 3: Optimized Solution (5 minutes)
Use recursive DFS: for each node, recursively invert the left and right subtrees, then swap the left and right children of the current node. Base case: if node is null, return null. This approach uses O(h) space for the recursion stack where h is the height, and O(n) time to visit all nodes. The recursive solution is elegant, naturally handles the tree structure, and modifies the tree in-place without extra data structures beyond the call stack.
Solution Approach
This problem requires swapping the left and right children of every node in the tree. We can use a recursive DFS approach to traverse the tree and swap children at each node.
Key Insights:
- Swap Children: For each node, swap its left and right children
- Recursive Traversal: Process subtrees recursively
- Base Case: Empty node (null) requires no action
- In-Place Modification: Modify the tree structure directly
Solution: Recursive DFS
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(!root) return root;
TreeNode* tmp = root->left;
root->left = invertTree(root->right);
root->right = invertTree(tmp);
return root;
}
};
Algorithm Explanation:
- Base Case:
- If
rootisnullptr, returnroot(no action needed)
- If
- Swap Children:
- Store
root->leftin a temporary variabletmp - Recursively invert
root->rightand assign toroot->left - Recursively invert the original
root->left(stored intmp) and assign toroot->right
- Store
- Return: Return the modified root node
Why This Works?
The algorithm uses post-order traversal (process children before parent):
- First, recursively invert both subtrees
- Then swap the already-inverted subtrees
- This ensures all nodes below are inverted before swapping
Alternative interpretation: The algorithm swaps children and then recursively inverts the swapped subtrees, which is equivalent to inverting subtrees first then swapping.
Example Walkthrough:
Input: root = [4,2,7,1,3,6,9]
Initial tree:
4
/ \
2 7
/ \ / \
1 3 6 9
invertTree(4):
root = 4, has children
tmp = 2 (left child)
root->left = invertTree(7):
root = 7, has children
tmp = 6 (left child)
root->left = invertTree(9):
root = 9, leaf node
return 9
root->right = invertTree(6):
root = 6, leaf node
return 6
After swap: 7 has children (9, 6)
return 7
root->right = invertTree(2):
root = 2, has children
tmp = 1 (left child)
root->left = invertTree(3):
root = 3, leaf node
return 3
root->right = invertTree(1):
root = 1, leaf node
return 1
After swap: 2 has children (3, 1)
return 2
After swap: 4 has children (7, 2)
return 4
Final tree:
4
/ \
7 2
/ \ / \
9 6 3 1
Complexity Analysis:
- Time Complexity: O(n)
- Visit each node exactly once
- n = number of nodes in tree
- Space Complexity: O(h)
- Recursion call stack depth = height of tree
- Best case (balanced): O(log n)
- Worst case (skewed): O(n)
Alternative Approaches
Solution 2: Simplified Recursive DFS
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (!root) return nullptr;
TreeNode* left = invertTree(root->left);
TreeNode* right = invertTree(root->right);
root->left = right;
root->right = left;
return root;
}
};
Key Difference: More explicit - invert subtrees first, then swap
Complexity: Same as Solution 1
Solution 3: Iterative DFS with Stack
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (!root) return nullptr;
stack<TreeNode*> st;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
// Swap children
TreeNode* tmp = node->left;
node->left = node->right;
node->right = tmp;
// Push children to stack
if (node->left) st.push(node->left);
if (node->right) st.push(node->right);
}
return root;
}
};
Complexity:
- Time: O(n)
- Space: O(h) for stack
Solution 4: Iterative BFS with Queue
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (!root) return nullptr;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
TreeNode* node = q.front();
q.pop();
// Swap children
TreeNode* tmp = node->left;
node->left = node->right;
node->right = tmp;
// Enqueue children
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
return root;
}
};
Complexity:
- Time: O(n)
- Space: O(w) where w is maximum width
Key Insights
- Post-order Traversal: Process children before parent (or swap then recurse)
- In-Place Modification: Modify tree structure directly, no need for new tree
- Symmetric Operation: Swapping is symmetric - order doesn’t matter
- Base Case: Empty node requires no action
- Return Root: Always return the root node after modification
Edge Cases
- Empty tree:
root = null→ returnnull - Single node:
root = [1]→ return[1](no change) - Skewed tree:
[1,2,null]→ becomes[1,null,2] - Balanced tree: Works correctly for any balanced tree
- Large tree: Handles up to 100 nodes efficiently
Common Mistakes
- Not handling null: Forgetting to check for empty node
// WRONG: TreeNode* tmp = root->left; // ❌ Crashes if root is null - Wrong traversal order: Processing parent before children (pre-order)
- Creating new nodes: Unnecessarily creating new tree instead of modifying in-place
- Not returning root: Forgetting to return the modified root
- Swapping before recursion: Should swap after or during recursion
Comparison of Approaches
| Approach | Time | Space | Pros | Cons |
|---|---|---|---|---|
| Recursive DFS (Provided) | O(n) | O(h) | Clean, concise | Recursion overhead |
| Simplified Recursive DFS | O(n) | O(h) | More explicit | Same as above |
| Iterative DFS | O(n) | O(h) | No recursion overhead | More code |
| Iterative BFS | O(n) | O(w) | Level-order traversal | More space for wide trees |
When to Use Each Approach
- Recursive DFS: Most common, simplest solution (recommended)
- Iterative DFS: When avoiding recursion or stack overflow concerns
- Iterative BFS: When level-order processing is preferred
Related Problems
- LC 226: Invert Binary Tree - This problem
- LC 100: Same Tree - Check if two trees are identical
- LC 101: Symmetric Tree - Check if tree is symmetric
- LC 104: Maximum Depth of Binary Tree - Find maximum depth
- LC 111: Minimum Depth of Binary Tree - Find minimum depth
- LC 617: Merge Two Binary Trees - Merge two trees
This problem demonstrates tree manipulation using recursive DFS. The key insight is swapping children at each node while recursively processing subtrees. It’s a fundamental tree operation that appears in many tree problems.