Topics › Binary Trees › Medium Problems
Check if two trees are identical or not
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
Given the roots p and q of two binary trees, return true if the trees are identical: they have exactly the same shape, and every pair of nodes in the same position holds the same value. Two empty trees are identical; an empty tree and a non-empty one are not.
Constraints: up to 100 nodes per tree in the LeetCode version (the methods below are linear, so much larger trees are fine), values fit in an int.
Example 1
p = [1, 2, 3] q = [1, 2, 3]
1 1
/ \ / \
2 3 2 3
answer = true
Example 2
p = [1, 2] q = [1, null, 2]
1 1
/ \
2 2
answer = false // same values, but 2 is a left child in p and a right child in q
Example 3
p = [1, 2, 1] q = [1, 1, 2]
answer = false // same shape, but the children's values are swapped
Example 2 is the trap. The two trees contain the same values, and even their preorder traversals agree (1, 2 in both). Equality has to capture structure as well as values, and every approach below is a different way of doing that.
Approach 1: Serialize and compare
Intuition. Turn each tree into a sequence that determines the tree uniquely, then compare the sequences. A plain traversal is not enough, as Example 2 shows. But a preorder traversal that writes an explicit marker (here null) for every missing child is unique: reading it back, the marker tells you exactly where each subtree ends, so you can rebuild the tree from it. Two trees are therefore identical if and only if their marked preorder sequences are equal.
For Example 2 the sequences are [1, 2, null, null, null] and [1, null, 2, null, null], which differ at position 1.
Algorithm.
- Serialize
pinto listaandqinto listb: at anullnode appendnull; otherwise append the value, then serialize the left child, then the right child. - Return
a.equals(b).
static boolean bySerialization(TreeNode p, TreeNode q) {
List<Integer> a = new ArrayList<>(), b = new ArrayList<>();
serialize(p, a);
serialize(q, b);
return a.equals(b);
}
static void serialize(TreeNode node, List<Integer> out) {
if (node == null) {
out.add(null); // the marker that pins down the shape
return;
}
out.add(node.val);
serialize(node.left, out);
serialize(node.right, out);
}
List.equals compares element by element with Objects.equals, so the null markers are handled safely and Integer values are compared by value, not by reference.
Complexity. Time O(n + m) for the two trees of sizes n and m: both are serialized completely even when they differ at the root. Space O(n + m) for the two lists plus O(h) for the recursion. The idea is valuable beyond this problem: comparing canonical encodings is how subtree matching and serialization problems are attacked.
Approach 2: Optimal, simultaneous recursion
Intuition. Walk both trees at the same time, always standing on the nodes in the same position. At each step there are only three situations:
- Both nodes are
null: these positions match. - Exactly one is
null: the shapes differ, so the answer isfalse. - Both exist: the values must be equal, and then the left subtrees must be identical and the right subtrees must be identical.
That is a complete recursive definition of “identical”, so the code is just that definition. The first two cases compress into one line: if either node is null, the trees match exactly when both are, which is p == q.
Algorithm.
- If
porqisnull, returnp == q. - Return
p.val == q.val && same(p.left, q.left) && same(p.right, q.right).
static boolean recursive(TreeNode p, TreeNode q) {
if (p == null || q == null) return p == q;
return p.val == q.val && recursive(p.left, q.left) && recursive(p.right, q.right);
}
Complexity. Time O(min(n, m)): the walk only visits positions present in both trees, and the && short-circuits at the first difference, so it stops early on mismatch. Space O(h) for the recursion, where h is the smaller height reached.
Dry run on Example 3:
| call | p node | q node | result |
|---|---|---|---|
| root | 1 | 1 | values equal, check left subtrees |
| left | 2 | 1 | values differ, return false |
| root | false (the right subtrees are never examined) |
Approach 3: Iterative, breadth-first over pairs
Intuition. The same three cases can be checked with a queue instead of recursion, which avoids deep call stacks on degenerate trees. The queue holds pairs of nodes taken from the same position in the two trees. Each pair is checked with the three cases above; if both exist and match, the pair of left children and the pair of right children are added.
Algorithm.
- Enqueue the pair
(p, q). - While the queue is not empty, dequeue
(a, b):- if both are
null, continue; - if exactly one is
nullor the values differ, returnfalse; - otherwise enqueue
(a.left, b.left)and(a.right, b.right).
- if both are
- Return
true.
static boolean iterative(TreeNode p, TreeNode q) {
Deque<TreeNode[]> queue = new ArrayDeque<>();
queue.add(new TreeNode[]{p, q});
while (!queue.isEmpty()) {
TreeNode[] pair = queue.poll();
TreeNode a = pair[0], b = pair[1];
if (a == null && b == null) continue;
if (a == null || b == null || a.val != b.val) return false;
queue.add(new TreeNode[]{a.left, b.left});
queue.add(new TreeNode[]{a.right, b.right});
}
return true;
}
ArrayDeque refuses null elements, but here the elements are two-slot arrays, and an array may contain null entries freely. That is why the pair is stored as TreeNode[] rather than enqueueing the nodes one by one.
Complexity. Time O(min(n, m)), stopping at the first mismatch. Space O(w), where w is the widest level reached.
Edge cases and pitfalls
- Both trees empty. Identical. The recursive base case returns
null == null, which istrue; the queue version skips the(null, null)pair and returnstrue. - One tree empty. Not identical. The Java tests run every method with the arguments in both orders, since the relation is symmetric and a lopsided
nullcheck is a common bug. - Comparing values only. Matching value multisets, inorder sequences or unmarked preorder sequences is not enough.
[2, 2]and[2, null, 2]both give the preorder2, 2and are in the tests. - Comparing references.
p == qfor two non-null nodes tests whether they are the same object, not whether the trees are equal. It is only correct in the base case, where at least one side isnull. - A tree with itself. Must be
true; the Java file checks this as well.
Related problems
- Symmetric Binary Tree is the same simultaneous walk, comparing a tree with its own mirror image.
- Subtree of Another Tree runs this check at every node of the larger tree.
- Serialize and De-serialize BT develops the marked-traversal encoding of Approach 1.
- Check for balanced binary tree is another recursion that combines the answers of both subtrees and stops at the first failure.