DSA sheet · Trees · Construction & serialization pattern

Invert Binary Tree

This is the first problem of a new pattern in the sheet: construction / serialization, where we build, rebuild or reshape a tree. Inverting is the gentlest start. We don't make new nodes, we just swap the left and right child at every node, and the tree becomes its own mirror image. The teacher also uses this video to show how to think your way to code: write the part you understand, then let the dry run tell you what's missing. She solves it with DFS (her favourite here), then BFS.

Every problem below follows the same order:
① the question in simple words → ② what the constraints tell us → ③ intuition → ④ building the conditions from examples → ⑤ approach steps → ⑥ code → ⑦ code line by line → ⑧ dry run → ⑨ complexity & remember

Part 0 · Before starting

Part A · Invert Binary Tree with DFS

LeetCode 226

1The question in simple words

Given the root of a binary tree, turn it into its mirror image and return the root.

before
        4
      /   \
     2     7
    / \   / \
   1   3 6   9
after (mirror)
        4
      /   \
     7     2
    / \   / \
   9   6 3   1

The root 4 stays where it is. 2 was the left child and is now the right child. 7 was the right child and is now the left child. And below them, 1 and 3 also swapped, and so did 6 and 9.

Doubt 1: isn't it enough to swap just the root's two children?
→ No. If you only swap at the root, you get 4 → (7, 2), but 2 still has 1 on its left and 3 on its right. In a mirror, 1 must be on the right now. So the same swap has to happen at every node: the root, then 2, then 7, then their children, and so on down to the leaves.

2What the constraints tell us

3Intuition: a swap at every node

Stand on a node. You can see its left child and its right child. Swap them, meaning the whole branches, not just the numbers. Then walk into each child and do the same thing there. When every node has swapped its two children, the whole tree is mirrored.

Doubt 2: do we swap the values or the nodes?
→ The nodes (the links). We change which node root.left points to and which node root.right points to. When we move the node 2 to the right, everything hanging under 2 (1 and 3) moves with it in one go. Swapping just the numbers 2 and 7 would leave their children behind on the wrong sides.

4Building the code step by step (the teacher's way)

Her tip: don't try to write the whole code in one go. Write the parts you understand. Later, when you dry-run it, you'll see if a line needs to move, or if a variable or line is missing. That's exactly what happens here.

Step 1: the base case, and what should it return?

We know DFS needs "if the node is None, return…" but return what? Think about what the parent wants from its children. Does the parent need some number back, like a height or a sum? No. The parent just wants its two children swapped. So at first she writes a plain return.

Later, when she runs the Java code, it fails to compile. The function's return type is TreeNode, so it must return something. She changes it to return null. It also covers the case where the whole tree is empty: the answer for an empty tree is an empty tree.

Doubt 3: in Python, is a plain return OK?
→ It works, because a plain return gives back None in Python. But write return None to make it clear that "empty tree in, empty tree out". That's the same fix the teacher made.

Step 2: swap the two children of the current node

We're standing on the root, so we can reach root.left and root.right. Swap them with a temp variable, like swapping two numbers:

  1. temp = root.left: keep the left branch (2 with 1, 3) safe.
  2. root.left = root.right: now the left link points at 7.
  3. root.right = temp: the right link gets the saved 2-branch.

The teacher draws the tree between lines 2 and 3. It's a great way to see why the temp is needed:

after line 2 (half-way)
        4
      /   \
     7     7      ← same 7-branch twice!
    / \   / \
   6   9 6   9

  temp → 2 (1, 3)  ← still safe
after line 3
        4
      /   \
     7     2
    / \   / \
   6   9 1   3

Half-way through, both links point at the 7-branch, and the 2-branch survives only because temp holds it. Without the temp, the 2-branch would be lost forever.

Step 3: the root is right now, but the lower levels aren't

Look at the "after line 3" picture. 4, 7 and 2 are in the right places, but under 7 we have 6, 9, and the mirror needs 9, 6. Under 2 we have 1, 3 instead of 3, 1. So we repeat the same job for each child: call the same function on root.left and on root.right.

Step 4: what to return at the end?

The question wants the root of the inverted tree back, so the last line is return root.

Doubt 4: the two recursive calls also return a node (the 7 and the 2), but we don't store those anywhere. Is that a bug?
→ No. Those calls change the tree in place, by changing links on nodes that are already attached. So we don't need their return values. The return root matters only for the very first call, the one LeetCode makes. LeetCode takes that root and checks the tree from it.
Doubt 5: can I recurse first and swap afterwards?
→ Yes. Swapping before the calls (the teacher's order, like preorder) and swapping after both calls (like postorder) both work, because each node's swap happens exactly once either way. What you must not do is call left, then swap, then call "right". After the swap, "right" is the branch you already inverted, so you would undo it.

5Approach steps

  1. If the node is None → return None.
  2. Swap its left and right children (temp variable).
  3. Invert the left child's subtree, then the right child's subtree (recursively).
  4. Return the node.

6Code (Python)

Invert Binary Tree with DFS (the teacher's way)
class Solution:
    def invertTree(self, root):
        if root is None:              # empty spot: nothing to swap
            return None
        temp = root.left              # keep the left branch safe
        root.left = root.right        # right branch moves to the left
        root.right = temp             # old left branch moves to the right
        self.invertTree(root.left)    # now mirror everything below, left...
        self.invertTree(root.right)   # ...and right
        return root                   # the caller wants the root back
same idea, Python-style short version
class Solution:
    def invertTree(self, root):
        if root is None:
            return None
        # invert both children, then attach them on the opposite sides
        root.left, root.right = self.invertTree(root.right), self.invertTree(root.left)
        return root

The short version uses the return values: "the inverted right branch becomes my left, the inverted left branch becomes my right". Python evaluates both calls before assigning, so no temp is needed.

7Code line by line

linewhat it means
if root is None: return NoneBase case. Stops the recursion below leaves. Also answers the empty-tree case (0 nodes allowed).
temp = root.leftSave the whole left branch before we overwrite its link.
root.left = root.rightThe left link now points to the right branch. (For a moment both links point to it.)
root.right = tempThe right link gets the saved old left branch. This node's swap is done.
self.invertTree(root.left)Mirror everything inside the new left branch.
self.invertTree(root.right)Mirror everything inside the new right branch.
return rootGive the (same) root back to whoever called us. It matters for the first call.

8Dry run using the call stack

Tree 4 → (2 → 1, 3), (7 → 6, 9). "N" = None.

  1. inv(4): swap → 4's left is 7, right is 2. Call inv(4.left) = inv(7). inv(4) waits.
  2. inv(7): swap 6 and 9 → 7 → (9, 6). Call inv(9).
  3. inv(9): a leaf. Swap None with None (no change). inv(N) → None, inv(N) → None. Return 9.
  4. Back in inv(7): call inv(6), also a leaf → nothing changes → return 6. inv(7) returns 7 and leaves the stack.
  5. Back in inv(4): call inv(4.right) = inv(2). Swap 1 and 3 → 2 → (3, 1).
  6. inv(3) and inv(1) are leaves → nothing changes. inv(2) returns 2.
  7. inv(4) returns 4. The tree is now 4 → (7 → 9, 6), (2 → 3, 1). ✓ That's the mirror.
step 3 (deepest)
inv(4) ✓swappedinv(7) ✓swappedinv(9)inv(N) → None
step 5
inv(4) ✓swappedinv(2) swapping 1↔3
after step 1
      4
    /   \
   7     2
  / \   / \
 6   9 1   3
after step 2
      4
    /   \
   7     2
  / \   / \
 9   6 1   3
after step 5 (final)
      4
    /   \
   7     2
  / \   / \
 9   6 3   1

9Complexity & remember

Remember Invert (DFS)None → return None · swap left and right (temp!) · recurse on both · return root. Swap links, not values. Do it at every node.

Part B · Invert Binary Tree with BFS

Same question and the same swap. Only the order we visit nodes changes: level by level with a queue, the way we did level order traversal before.

1Intuition

Put 4 in the queue. When we pop 4, we don't need its value for anything. We just swap its two children, exactly as in DFS. Then we push those children (now 7, 2) so that later we pop them and swap their children. Every node is popped once and swapped once.

2Constraints for BFS

Zero nodes is allowed. If the root is None, we must return None before we start, or we'd pop None and crash on None.left.

3Approach steps

  1. If root is None → return None.
  2. Queue starts with the root.
  3. While the queue isn't empty: pop a node and swap its left and right children.
  4. Push the children that exist (left, then right).
  5. When the queue is empty → return root.

4Code (Python)

Invert Binary Tree with BFS
from collections import deque

class Solution:
    def invertTree(self, root):
        if root is None:                  # 0 nodes is allowed
            return None
        queue = deque([root])
        while queue:
            node = queue.popleft()
            temp = node.left              # same swap as DFS
            node.left = node.right
            node.right = temp
            if node.left:                 # line up the next level
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        return root

5Code line by line

linewhat it means
if root is None: return NoneBFS base case, needed only because the tree can be empty.
queue = deque([root])Start with level 0.
node = queue.popleft()Take the next node in level order.
temp / left / right linesThe same three-line swap as DFS. This node is done.
if node.left: queue.append(...)Push only real children. They are waiting to have their children swapped.
return rootEvery node was swapped, so give back the root.

6Dry run: watch the queue

start4
step 1pop 4 → swap: 4 → (7, 2) → push 7, 2
72
step 2pop 7 → swap: 7 → (9, 6) → push 9, 6
296
step 3pop 2 → swap: 2 → (3, 1) → push 3, 1
9631
steps 4–7pop 9, 6, 3, 1: leaves, so each swaps None with None and pushes nothing
endqueue empty → return root → 4 → (7 → 9, 6), (2 → 3, 1) ✓

Order of swaps: 4, 7, 2, then the leaves. That's level by level. DFS swapped in the order 4, 7, 9, 6, 2, 3, 1. Different order, same final tree.

7Complexity & DFS vs BFS

Both are efficient. The teacher still prefers DFS for inverting trees: it's shorter, and the stack only grows with the height, not the width.

Remember Invert (BFS)Check for an empty tree · queue with the root · pop → swap children → push real children · return root. Space is the tree's width.

Part C · Revision page

DFS (recursion)BFS (queue)
how we reach the next nodescall on root.left, root.rightpush children into the queue
work at each nodethe same 3-line swap (temp → left = right → right = temp)
base casealways: None → return Noneonly because n can be 0
order of swaps (example)4, 7, 9, 6, 2, 3, 14, 7, 2, 9, 6, 3, 1
timeO(n)O(n)
spaceO(h): heightO(w): width
teacher's pickpreferredalso fine
If you remember only 5 lines 1. Mirror = swap left and right at every node, not just the root.
2. Swap the links (whole branches), using a temp (or a, b = b, a).
3. DFS: None → None, swap, recurse both sides, return root.
4. BFS: pop, swap, push real children, return root.
5. DFS space = height, BFS space = width. Both O(n) time.
Mistakes to avoid ✗ swapping only at the root
✗ swapping values instead of nodes (children stay on the wrong side)
✗ root.left = root.right; root.right = root.left without a temp (both sides end up the same)
✗ "recurse left, swap, recurse right" (the right call re-inverts the branch you just did)
✗ forgetting return root at the end, or forgetting the empty-tree check in BFS
test it yourself (paste under any of the solutions above)
def level_order(root):
    out, q = [], [root]
    while q:
        n = q.pop(0)
        if n:
            out.append(n.val)
            q += [n.left, n.right]
    return out

root = TreeNode(4, TreeNode(2, TreeNode(1), TreeNode(3)), TreeNode(7, TreeNode(6), TreeNode(9)))
s = Solution()
print(level_order(s.invertTree(root)))                        # [4, 7, 2, 9, 6, 3, 1]
print(level_order(s.invertTree(TreeNode(2, TreeNode(1), TreeNode(3)))))   # [2, 3, 1]
print(s.invertTree(None))                                     # None

Based on this video: Invert Binary Tree | DFS & BFS