Binary Trees · 35 of 35
Tree Isomorphism Problem
Decide if one tree can be turned into another only by swapping the two children of some nodes, like turning mobiles around.
- Simple way
- time O(n * m)
room O(h) - Clever way
- time O(n + m)
room O(n + m)
The problem, simply
You get two binary trees. A flip at a node swaps its left and right child (each child keeps its own family). The two trees are isomorphic if you can make the first one look exactly like the second by flipping any nodes you like, any number of times. Values never change.
Return true or false.
A story to remember it
Picture it
Flip the root (2 and 3 trade places) and flip 2 (4 and 5 trade places). Tree 1 then looks exactly like tree 2. Node 3 has its child on the other side too, which is a flip at 3.
- checking
Examples
Several flips
- In
tree 1 = [1, 2, 3, 4, 5, 6], tree 2 = [1, 3, 2, null, 6, 5, 4]- Out
true
Flips at 1, 2 and 3 turn tree 1 into tree 2.
A value differs
- In
tree 1 = [1, 2, 3], tree 2 = [1, 3, 4]- Out
false
Tree 1 has a 2 and tree 2 does not. No flip can change a value.
Same values, different shape
- In
tree 1 = [1, 2, 3, 4], tree 2 = [1, 2, 3, null, null, 4]- Out
false
In tree 1, 4 hangs under 2. In tree 2, 4 hangs under 3. Flips never move a child to another parent.
Empty trees
- In
tree 1 = [], tree 2 = []- Out
true
Two empty trees are the same. One empty and one not is false.
Way 1: the simple way
Compare the two roots. If the values match, try both ways for the children: straight (left with left, right with right) or flipped (left with right, right with left). If either way works all the way down, the trees match.
- Both empty: true. Only one empty: false.
- Values differ: false.
- Return
(same(a.left, b.left) and same(a.right, b.right))or(same(a.left, b.right) and same(a.right, b.left)).
At the roots: straight compares 2 with 3, which fails at once. Flipped compares 2 with 2 and 3 with 3, and keeps going down.
- straight
- fails
- flipped
- keep checking
- looking now
- checking
- done
class Solution {
public boolean isIsomorphic(TreeNode a, TreeNode b) {
if (a == null && b == null) return true;
if (a == null || b == null) return false;
if (a.val != b.val) return false;
return (isIsomorphic(a.left, b.left) && isIsomorphic(a.right, b.right)) // straight
|| (isIsomorphic(a.left, b.right) && isIsomorphic(a.right, b.left)); // flipped
}
}How long it takes: when all values are different, the wrong pairing fails right at the first values, so it is about O(n). With repeated values, both pairings can go deep. Each pair of nodes (one from each tree) is compared at most once, so the worst case is O(n * m). Extra room: the call stack, O(h).
Way 2: the clever way
Give every subtree a name that ignores flips. Then two trees match exactly when their roots get the same name.
- An empty spot is named 0.
- For a node, first name its two children, say
xandy. - Its shape is the triple (value, smaller of
xandy, bigger ofxandy). Putting the smaller name first is what makes a flip invisible. - Look the shape up in a shared shape book (a hash map). If it is new, give it the next free number. That number is the node's name.
- Name both trees with the same book and compare the two root names.
Name every subtree, flips do not change the name
01 / 14
Give every subtree a short name that does not care about left and right. An empty spot is called 0. We fill a shared shape book as we go.
- seen
- done
- answer
- just added
import java.util.*;
class Solution {
private Map<String, Integer> book = new HashMap<>(); // shape -> name
public boolean isIsomorphic(TreeNode a, TreeNode b) {
book.clear();
return name(a) == name(b); // same book for both trees
}
private int name(TreeNode node) {
if (node == null) return 0; // an empty spot is named 0
int x = name(node.left), y = name(node.right);
String shape = node.val + "," + Math.min(x, y) + "," + Math.max(x, y); // order does not matter
Integer id = book.get(shape);
if (id == null) {
id = book.size() + 1; // a new shape gets the next number
book.put(shape, id);
}
return id;
}
}How long it takes: each node is named once, with one map lookup, about O(n + m). Extra room: the shape book, O(n + m).
Watch out for
- Check
a == null && b == nullbeforea == null || b == null, or two empty trees return false. - In Way 1, use
||between the two pairings and&&inside each. Mixing them up accepts trees where the left fits one way and the right fits the other way. - In Way 2, both trees must use the same shape book. Two separate books hand out numbers in a different order.
What interviewers ask next
- Flip equivalent trees (LeetCode 951) is this exact problem.
- Are two trees mirror images? Only the flipped pairing is allowed at every node.
- Group many trees into "same up to flips" families. Name them all with one shared book; trees with the same root name are in the same family. Way 1 would compare every pair.
- Unlabelled trees, or trees where nodes have any number of children. Sort the children's names instead of taking min and max. This is the classic AHU idea for checking if two rooted trees have the same shape.
Remember this
Grown-up words: spinning a bar is flipping the children, the short name is a canonical form, the shape book is a hash map used for hashing subtrees, and the two kinds of check are a depth first search with two branches.