226. Invert Binary Tree
Problem Description
You are given the root of a binary tree. Your task is to invert the tree and return its root.
Inverting a binary tree means swapping the left and right children of every node in the tree. This operation should be performed recursively throughout the entire tree structure.
For example, if you have a tree where a node has a left child L
and a right child R
, after inversion, that same node would have R
as its left child and L
as its right child. This swapping process applies to every single node in the tree, transforming the original tree into its mirror image.
The solution uses a recursive approach that:
- Checks if the current node (
root
) isNone
- if so, returnsNone
- Recursively inverts the left subtree and stores it in variable
l
- Recursively inverts the right subtree and stores it in variable
r
- Swaps the children by assigning the inverted right subtree (
r
) toroot.left
and the inverted left subtree (l
) toroot.right
- Returns the current
root
node with its children now swapped
This bottom-up recursion ensures that all subtrees are inverted before their parent nodes, resulting in a completely inverted binary tree.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: A binary tree is a special type of graph where each node has at most two children and there are no cycles.
Is it a tree?
- Yes: The problem explicitly states we're working with a binary tree structure.
DFS
- Yes: Since we identified this as a tree problem, the flowchart leads us to DFS (Depth-First Search).
Conclusion: The flowchart suggests using a DFS approach for inverting the binary tree.
This makes perfect sense because:
- We need to visit every node in the tree to perform the inversion
- DFS allows us to recursively traverse to the leaf nodes first, then work our way back up
- The recursive nature of DFS aligns perfectly with the recursive structure of trees
- We can invert subtrees bottom-up: first invert the left and right subtrees, then swap them at the current node
The solution implements DFS through recursion - it traverses down to the leaf nodes, inverts them (base case returns None
for null nodes), and then swaps the left and right children as it returns back up the call stack. This is a classic application of the DFS pattern on a tree structure.
Intuition
When we think about inverting a binary tree, we're essentially creating a mirror image of the original tree. Imagine looking at a tree in a mirror - everything on the left appears on the right, and vice versa.
The key insight is that this mirroring operation needs to happen at every level of the tree. Each node's left and right children need to be swapped. But here's the crucial part: before we swap a node's children, those children themselves (which are subtrees) also need to be inverted first.
This naturally leads us to think recursively: to invert a tree rooted at a node, we first need to invert its left subtree and its right subtree independently, then swap these already-inverted subtrees.
Why does this work? Consider the recursive structure:
- If we have a leaf node (no children), there's nothing to invert
- If we have
None
(empty tree), we returnNone
- For any other node, we break down the problem into smaller subproblems: invert the left subtree, invert the right subtree, then swap them
The beauty of this approach is that by the time we swap the children at any node, we've already ensured that those children (and all their descendants) have been properly inverted. This bottom-up approach guarantees that the entire tree gets inverted correctly.
Think of it like renovating a building floor by floor from bottom to top - you complete each floor before moving to the next one. Similarly, we complete the inversion of smaller subtrees before handling their parent nodes, eventually inverting the entire tree when we reach the root.
Solution Approach
The solution implements a recursive DFS approach to invert the binary tree. Let's walk through the implementation step by step:
Base Case:
The first check if root is None: return None
handles the base case. When we encounter a null node (empty subtree), we simply return None
. This stops the recursion and handles leaf nodes' null children.
Recursive Calls:
l, r = self.invertTree(root.left), self.invertTree(root.right)
This line makes two recursive calls:
self.invertTree(root.left)
recursively inverts the entire left subtree and stores the result inl
self.invertTree(root.right)
recursively inverts the entire right subtree and stores the result inr
The key insight here is that these recursive calls will completely invert their respective subtrees before returning. By the time we get the values of l
and r
, they represent fully inverted subtrees.
The Swap:
root.left, root.right = r, l
This is where the actual inversion happens at the current node level. We assign:
- The inverted right subtree (
r
) to become the new left child - The inverted left subtree (
l
) to become the new right child
This Python syntax performs a simultaneous assignment, elegantly swapping the children in one line.
Return the Root:
Finally, return root
returns the current node with its children now swapped. As the recursion unwinds back up the tree, each level performs this swap, ultimately returning the root of the completely inverted tree.
The algorithm visits each node exactly once, making it an O(n) time complexity solution where n is the number of nodes. The space complexity is O(h) where h is the height of the tree, due to the recursive call stack.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through inverting a small binary tree step by step to illustrate the solution approach.
Initial Tree:
4 / \ 2 7 / \ \ 1 3 9
Step 1: Start at root (4)
- We call
invertTree(4)
- Node 4 is not None, so we proceed to recursive calls
- We call
invertTree(2)
for the left subtree andinvertTree(7)
for the right subtree
Step 2: Process left subtree rooted at 2
- In
invertTree(2)
:- Node 2 is not None
- Call
invertTree(1)
for left child andinvertTree(3)
for right child
Step 3: Process leaf nodes 1 and 3
invertTree(1)
:- Node 1 is not None
- Call
invertTree(None)
for both children → returns None for both - Swap: 1.left = None, 1.right = None (no change since both are None)
- Return node 1
invertTree(3)
:- Similar process, returns node 3
Step 4: Complete inversion of subtree rooted at 2
- Back in
invertTree(2)
:- l = node 1 (inverted left subtree)
- r = node 3 (inverted right subtree)
- Swap: 2.left = 3, 2.right = 1
- Return node 2 (now with children swapped)
Step 5: Process right subtree rooted at 7
- In
invertTree(7)
:- Node 7 is not None
- Call
invertTree(None)
for left child → returns None - Call
invertTree(9)
for right child invertTree(9)
processes and returns node 9 (leaf node)- Swap: 7.left = 9, 7.right = None
- Return node 7 (now with children swapped)
Step 6: Complete inversion at root
- Back in
invertTree(4)
:- l = inverted subtree rooted at 2 (which now has 3 on left, 1 on right)
- r = inverted subtree rooted at 7 (which now has 9 on left, None on right)
- Swap: 4.left = node 7, 4.right = node 2
- Return node 4
Final Inverted Tree:
4 / \ 7 2 / / \ 9 3 1
The recursion ensures that each subtree is fully inverted before being swapped at its parent level, working from the bottom up to create the complete mirror image of the original tree.
Solution Implementation
1# Definition for a binary tree node.
2# class TreeNode:
3# def __init__(self, val=0, left=None, right=None):
4# self.val = val
5# self.left = left
6# self.right = right
7
8from typing import Optional
9
10class Solution:
11 def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
12 """
13 Inverts a binary tree by swapping left and right subtrees recursively.
14
15 Args:
16 root: The root node of the binary tree to invert.
17
18 Returns:
19 The root of the inverted binary tree.
20 """
21 # Base case: if the node is None, return None
22 if root is None:
23 return None
24
25 # Recursively invert the left and right subtrees
26 inverted_left = self.invertTree(root.left)
27 inverted_right = self.invertTree(root.right)
28
29 # Swap the left and right children of the current node
30 root.left = inverted_right
31 root.right = inverted_left
32
33 # Return the root node with its children swapped
34 return root
35
1/**
2 * Definition for a binary tree node.
3 * public class TreeNode {
4 * int val;
5 * TreeNode left;
6 * TreeNode right;
7 * TreeNode() {}
8 * TreeNode(int val) { this.val = val; }
9 * TreeNode(int val, TreeNode left, TreeNode right) {
10 * this.val = val;
11 * this.left = left;
12 * this.right = right;
13 * }
14 * }
15 */
16class Solution {
17 /**
18 * Inverts a binary tree by swapping left and right children recursively.
19 *
20 * @param root The root node of the binary tree to invert
21 * @return The root of the inverted binary tree
22 */
23 public TreeNode invertTree(TreeNode root) {
24 // Base case: if the node is null, return null
25 if (root == null) {
26 return null;
27 }
28
29 // Recursively invert the left subtree
30 TreeNode invertedLeft = invertTree(root.left);
31
32 // Recursively invert the right subtree
33 TreeNode invertedRight = invertTree(root.right);
34
35 // Swap the left and right children of the current node
36 root.left = invertedRight;
37 root.right = invertedLeft;
38
39 // Return the current node with its children swapped
40 return root;
41 }
42}
43
1/**
2 * Definition for a binary tree node.
3 * struct TreeNode {
4 * int val;
5 * TreeNode *left;
6 * TreeNode *right;
7 * TreeNode() : val(0), left(nullptr), right(nullptr) {}
8 * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
9 * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
10 * };
11 */
12class Solution {
13public:
14 /**
15 * Inverts a binary tree by swapping left and right children recursively
16 * @param root - pointer to the root node of the binary tree
17 * @return pointer to the root of the inverted tree
18 */
19 TreeNode* invertTree(TreeNode* root) {
20 // Base case: if the node is null, return null
21 if (!root) {
22 return root;
23 }
24
25 // Recursively invert the left subtree
26 TreeNode* invertedLeft = invertTree(root->left);
27
28 // Recursively invert the right subtree
29 TreeNode* invertedRight = invertTree(root->right);
30
31 // Swap the left and right children of the current node
32 root->left = invertedRight;
33 root->right = invertedLeft;
34
35 // Return the root of the inverted tree
36 return root;
37 }
38};
39
1/**
2 * Definition for a binary tree node.
3 * class TreeNode {
4 * val: number
5 * left: TreeNode | null
6 * right: TreeNode | null
7 * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
8 * this.val = (val===undefined ? 0 : val)
9 * this.left = (left===undefined ? null : left)
10 * this.right = (right===undefined ? null : right)
11 * }
12 * }
13 */
14
15/**
16 * Inverts a binary tree by swapping left and right children recursively
17 * @param root - The root node of the binary tree to invert
18 * @returns The root of the inverted binary tree
19 */
20function invertTree(root: TreeNode | null): TreeNode | null {
21 // Base case: if the node is null, return null
22 if (!root) {
23 return root;
24 }
25
26 // Recursively invert the left subtree
27 const leftSubtree: TreeNode | null = invertTree(root.left);
28
29 // Recursively invert the right subtree
30 const rightSubtree: TreeNode | null = invertTree(root.right);
31
32 // Swap the left and right children of the current node
33 root.left = rightSubtree;
34 root.right = leftSubtree;
35
36 // Return the root node with its children swapped
37 return root;
38}
39
Time and Space Complexity
Time Complexity: O(n)
The algorithm visits each node in the binary tree exactly once. At each node, it performs a constant amount of work (swapping the left and right child references). Since there are n
nodes in the tree and each node is processed once, the total time complexity is O(n)
.
Space Complexity: O(n)
The space complexity comes from the recursive call stack. In the worst case, when the tree is completely unbalanced (e.g., a skewed tree that resembles a linked list), the recursion depth can be O(n)
. In the best case of a balanced tree, the recursion depth would be O(log n)
. However, we consider the worst-case scenario for space complexity analysis, which gives us O(n)
.
Additionally, the algorithm stores temporary references l
and r
for the inverted subtrees, but these don't add to the overall space complexity as they are constant space operations at each recursive level.
Common Pitfalls
1. Attempting to Swap Before Recursive Calls
A common mistake is trying to swap the children before making the recursive calls:
Incorrect Implementation:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root is None:
return None
# WRONG: Swapping before recursion
root.left, root.right = root.right, root.left
self.invertTree(root.left)
self.invertTree(root.right)
return root
Why This Fails:
While this might seem logical, it has a critical flaw. After swapping, when you call self.invertTree(root.left)
, you're actually processing what was originally the right subtree. The recursive calls don't return values that get assigned back to the children, so you lose the reference to the inverted subtrees.
Solution: Always store the results of recursive calls and then perform the swap:
left = self.invertTree(root.left) right = self.invertTree(root.right) root.left, root.right = right, left
2. Modifying References During Traversal
Another pitfall is trying to swap while simultaneously traversing:
Incorrect Implementation:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root is None:
return None
# WRONG: Directly assigning recursive results without temporary variables
root.left = self.invertTree(root.right)
root.right = self.invertTree(root.left) # root.left has already been modified!
return root
Why This Fails:
After the first assignment root.left = self.invertTree(root.right)
, the original left subtree reference is lost. The second line then processes the already-modified root.left
(which now contains the inverted right subtree), leading to incorrect results.
Solution: Store both recursive results before any assignments:
l, r = self.invertTree(root.left), self.invertTree(root.right) root.left, root.right = r, l
3. Forgetting to Return the Root
Incorrect Implementation:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root is None:
return None
l, r = self.invertTree(root.left), self.invertTree(root.right)
root.left, root.right = r, l
# WRONG: Forgetting to return root
Why This Fails:
Without returning root
, the function returns None
by default in Python, causing the entire tree structure to be lost as the recursion unwinds.
Solution: Always return the modified root node to maintain the tree structure:
return root
Which two pointer techniques do you use to check if a string is a palindrome?
Recommended Readings
Everything About Trees A tree is a type of graph data structure composed of nodes and edges Its main properties are It is acyclic doesn't contain any cycles There exists a path from the root to any node Has N 1 edges where N is the number of nodes in the tree and
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
https assets algo monster cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell Breadth First Search BFS
Want a Structured Path to Master System Design Too? Don’t Miss This!