🌲 Binary Tree Magic: Transforming Trees Like a Wizard!
Imagine you have a magical family tree made of toy blocks. Each block can have two children hanging below it—one on the left, one on the right. Today, we’re going to learn six amazing spells to transform, compare, build, and reshape these trees!
🪞 Spell 1: Invert Binary Tree (The Mirror Spell)
What is it?
Imagine holding your toy tree in front of a mirror. Everything flips! What was on the left now appears on the right, and vice versa. That’s exactly what “inverting” a binary tree means.
The Simple Idea
For every single block in your tree, swap its left and right children. That’s it! Do this for all blocks, and your entire tree becomes a mirror image.
graph TD subgraph Before A1["4"] --> B1["2"] A1 --> C1["7"] B1 --> D1["1"] B1 --> E1["3"] C1 --> F1["6"] C1 --> G1["9"] end
graph TD subgraph After Mirror A2["4"] --> C2["7"] A2 --> B2["2"] C2 --> G2["9"] C2 --> F2["6"] B2 --> E2["3"] B2 --> D2["1"] end
Python Code
def invertTree(root):
if root is None:
return None
# Swap left and right
root.left, root.right = (
root.right, root.left
)
# Do the same for children
invertTree(root.left)
invertTree(root.right)
return root
Why does this work?
We visit every block (node), swap its children, then tell each child to do the same. It’s like a chain of mirror spells going down the whole tree!
👯 Spell 2: Same Tree & Symmetric Tree
Same Tree: Are Two Trees Identical Twins?
Two trees are the same if:
- Both are empty (no blocks at all), OR
- Both have the same value at the top, AND their left subtrees are the same, AND their right subtrees are the same.
def isSameTree(p, q):
# Both empty? Same!
if not p and not q:
return True
# One empty, one not? Different!
if not p or not q:
return False
# Values different? Different!
if p.val != q.val:
return False
# Check both sides
return (isSameTree(p.left, q.left)
and isSameTree(p.right, q.right))
Symmetric Tree: Is One Tree a Mirror of Itself?
A tree is symmetric if its left side is a mirror image of its right side. Like a butterfly’s wings!
graph TD A["1"] --> B["2"] A --> C["2"] B --> D["3"] B --> E["4"] C --> F["4"] C --> G["3"]
The left subtree [2,3,4] mirrors the right subtree [2,4,3].
def isSymmetric(root):
def isMirror(t1, t2):
if not t1 and not t2:
return True
if not t1 or not t2:
return False
return (t1.val == t2.val
and isMirror(t1.left, t2.right)
and isMirror(t1.right, t2.left))
return isMirror(root, root)
The Secret Trick
For symmetry, we compare left’s left with right’s right, and left’s right with right’s left. It’s like checking if a butterfly’s spots match!
🔍 Spell 3: Subtree of Another Tree
What is a Subtree?
Imagine you have a big LEGO castle (Tree A). Your friend has a small LEGO tower (Tree B). Is your friend’s tower hidden somewhere inside your castle? If yes, Tree B is a subtree of Tree A!
How to Find It
- Check if Tree B matches Tree A starting at the root
- If not, check if Tree B is hiding in A’s left subtree
- If not, check if Tree B is hiding in A’s right subtree
def isSubtree(root, subRoot):
if not root:
return False
# Check if trees match here
if isSameTree(root, subRoot):
return True
# Search in left and right
return (isSubtree(root.left, subRoot)
or isSubtree(root.right, subRoot))
Example
graph TD subgraph Big Tree A["3"] --> B["4"] A --> C["5"] B --> D["1"] B --> E["2"] end
graph TD subgraph Small Tree X["4"] --> Y["1"] X --> Z["2"] end
The small tree [4,1,2] is found inside the big tree!
🏗️ Spell 4: Building Trees from Traversals
The Puzzle
Imagine someone tells you: “I visited all the blocks in a tree and wrote down their values in two different orders.” Can you rebuild the original tree? Yes, you can!
The Three Ways to Walk a Tree
- Preorder: Visit parent FIRST, then left, then right →
[Parent, Left..., Right...] - Inorder: Visit left FIRST, then parent, then right →
[Left..., Parent, Right...] - Postorder: Visit left, then right, then parent LAST →
[Left..., Right..., Parent]
Building from Preorder + Inorder
def buildTree(preorder, inorder):
if not preorder or not inorder:
return None
# First in preorder is always root
root_val = preorder[0]
root = TreeNode(root_val)
# Find root in inorder
mid = inorder.index(root_val)
# Left of mid = left subtree
# Right of mid = right subtree
root.left = buildTree(
preorder[1:mid+1],
inorder[:mid]
)
root.right = buildTree(
preorder[mid+1:],
inorder[mid+1:]
)
return root
The Magic Recipe
| You Have | You Can Build |
|---|---|
| Preorder + Inorder | âś… Yes! |
| Inorder + Postorder | âś… Yes! |
| Preorder + Postorder | ⚠️ Only if no single-child nodes |
Example
- Preorder:
[3, 9, 20, 15, 7] - Inorder:
[9, 3, 15, 20, 7]
Step 1: 3 is root (first in preorder)
Step 2: In inorder, 9 is left of 3, so [9] is left subtree
Step 3: [15, 20, 7] is right subtree
Step 4: Repeat for each subtree!
📦 Spell 5: Serialization & Deserialization
The Problem
How do you save a tree to a file and load it back later? Or send it over the internet? You need to convert it to text (serialize) and back to a tree (deserialize).
The Strategy
Use preorder traversal and mark empty spots with a special symbol like #.
graph TD A["1"] --> B["2"] A --> C["3"] B --> D["#"] B --> E["#"] C --> F["4"] C --> G["5"]
Serialized: "1,2,#,#,3,4,#,#,5,#,#"
Python Code
class Codec:
def serialize(self, root):
result = []
def preorder(node):
if not node:
result.append("#")
return
result.append(str(node.val))
preorder(node.left)
preorder(node.right)
preorder(root)
return ",".join(result)
def deserialize(self, data):
values = iter(data.split(","))
def build():
val = next(values)
if val == "#":
return None
node = TreeNode(int(val))
node.left = build()
node.right = build()
return node
return build()
Why This Works
By using # for empty spots, we know exactly where each subtree ends. It’s like leaving breadcrumbs to find your way back!
📏 Spell 6: Flatten Binary Tree to Linked List
The Goal
Transform a tree into a straight line—like unfolding origami back into flat paper. The line follows preorder (root, left, right).
Before and After
graph TD subgraph Before A["1"] --> B["2"] A --> C["5"] B --> D["3"] B --> E["4"] C --> F["#"] C --> G["6"] end
After flattening:
1 → 2 → 3 → 4 → 5 → 6
All nodes link through their right pointer. Left is always None.
The Clever Trick (Morris-like)
def flatten(root):
current = root
while current:
if current.left:
# Find rightmost of left subtree
rightmost = current.left
while rightmost.right:
rightmost = rightmost.right
# Connect rightmost to current's right
rightmost.right = current.right
# Move left subtree to right
current.right = current.left
current.left = None
current = current.right
Step-by-Step Example
- Start at
1, left child is2 - Find rightmost of left subtree:
4 - Connect
4.right → 5 - Move:
1.right = 2,1.left = None - Move to
2, repeat…
Result: A flat linked list using only right pointers!
🎯 Quick Summary
| Operation | What It Does | Key Insight |
|---|---|---|
| Invert | Mirror flip | Swap left & right everywhere |
| Same Tree | Check twins | Compare values + both sides |
| Symmetric | Self-mirror | Compare left↔right crossed |
| Subtree | Find hidden tree | Search + compare at each node |
| Build Tree | Reconstruct | Use traversal order clues |
| Serialize | Save to text | Mark empty spots with # |
| Flatten | Make a line | Rewire rights, clear lefts |
🌟 You’re Now a Tree Wizard!
You’ve learned six powerful spells to manipulate binary trees. Each one builds on a simple idea:
- Think recursively—solve for one node, let children solve themselves
- Mark what’s missing—use
Noneor#as signals - Trust the pattern—the same logic works at every level
Go forth and transform trees with confidence! 🚀
