Topics › Binary Trees › Medium Problems
Check for symmetrical BTs
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
Given the root of a binary tree, decide whether it is symmetric, meaning it is a mirror image of itself around a vertical line through the root. Formally, the tree is symmetric when its left subtree is the mirror of its right subtree, where two trees A and B are mirrors of each other if
- both are empty, or
- both are non-empty, their roots have equal values,
A.leftmirrorsB.right, andA.rightmirrorsB.left.
An empty tree and a single node are symmetric.
Constraints (LeetCode 101): 1 to 1000 nodes, values in [-100, 100]. The Java file also runs a 131071-node tree.
Example 1
1
/ \
2 2
/ \ / \
3 4 4 3
answer = true
Fold the picture along the vertical line through 1: every node lands on a node with the same value.
Example 2
1
/ \
2 2
\ \
3 3
answer = false
Each level reads the same forwards and backwards if you only look at values (2 2, then 3 3), yet the tree is not symmetric: both 3s are right children, and the mirror of a right child is a left child. Symmetry is about shape and values together.
Approach 1: Build the mirror and compare
Intuition. “Symmetric” means “equal to its own mirror image”. So construct that mirror image explicitly: a copy of the tree in which every node’s children are swapped. Then check whether the original and the copy are identical, node by node. This is a direct translation of the definition and is easy to trust, which makes it a good first solution and a good test oracle.
Algorithm.
- Build
mirrorCopy(root): a new node with the same value whose left child is the mirror copy of the original right child, and vice versa. - Compare the original and the copy with the usual “identical trees” recursion: both
null, or equal values with identical left subtrees and identical right subtrees.
static boolean mirrorAndCompare(TreeNode root) {
return identical(root, mirrorCopy(root));
}
static TreeNode mirrorCopy(TreeNode node) {
if (node == null) return null;
TreeNode m = new TreeNode(node.val);
m.left = mirrorCopy(node.right); // children swap sides at every level
m.right = mirrorCopy(node.left);
return m;
}
static boolean identical(TreeNode a, TreeNode b) {
if (a == null || b == null) return a == b;
return a.val == b.val && identical(a.left, b.left) && identical(a.right, b.right);
}
Complexity. Time O(n): the copy visits each node once and the comparison visits each node at most once. Space O(n) for the copied tree, plus O(h) recursion. It also does redundant work: comparing the tree with its mirror checks every left/right pair twice, once from each side.
Approach 2: Compare the two halves directly (recursive)
Intuition. We never need the whole mirror copy. The definition already says what to compare: the left subtree against the right subtree as mirrors. Walk both halves at the same time with two pointers a (in the left half) and b (in the right half). Mirroring swaps sides, so the outer pair a.left and b.right must mirror each other, and so must the inner pair a.right and b.left.
Each recursive call compares one node from the left half with the node that should be its reflection, so every node is looked at once and nothing is copied.
Algorithm.
- An empty tree is symmetric. Otherwise return
isMirror(root.left, root.right). isMirror(a, b):- If either is
null, the pair is fine only if both arenull. - Otherwise require
a.val == b.val,isMirror(a.left, b.right)andisMirror(a.right, b.left).
- If either is
static boolean recursive(TreeNode root) {
return root == null || isMirror(root.left, root.right);
}
static boolean isMirror(TreeNode a, TreeNode b) {
if (a == null || b == null) return a == b; // both missing is fine, exactly one missing is not
return a.val == b.val
&& isMirror(a.left, b.right) // outer pair
&& isMirror(a.right, b.left); // inner pair
}
The line if (a == null || b == null) return a == b; covers three cases at once: both null gives true, and exactly one null gives false because a null reference never equals a real node.
Complexity. Time O(n): each node takes part in at most one comparison, and && stops at the first mismatch. Space O(h) for the recursion; no copy is made.
Approach 3: Same comparison with a queue (iterative)
Intuition. The recursion in Approach 2 is just a list of pairs still to be checked. We can keep those pairs in a queue instead of on the call stack. Start with the pair (root.left, root.right). Take a pair out; if both are null, it is fine; if exactly one is null or the values differ, the tree is not symmetric; otherwise add the outer pair and the inner pair. If the queue empties without a mismatch, the tree is symmetric.
A Java detail: ArrayDeque does not accept null elements, but we store arrays of two nodes. The array itself is never null, even when its entries are, so this is safe.
Algorithm.
- If the root is
null, returntrue. Enqueue{root.left, root.right}. - While the queue is not empty: dequeue
{a, b}.- Both
null: continue. - One
null, ora.val != b.val: returnfalse. - Otherwise enqueue
{a.left, b.right}and{a.right, b.left}.
- Both
- Return
true.
static boolean iterative(TreeNode root) {
if (root == null) return true;
Deque<TreeNode[]> queue = new ArrayDeque<>();
queue.add(new TreeNode[]{root.left, root.right});
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.right});
queue.add(new TreeNode[]{a.right, b.left});
}
return true;
}
Dry run on Example 2 (L and R are the left and right 2):
| dequeued pair | check | enqueued |
|---|---|---|
| (L, R) | both 2, equal | (L.left = null, R.right = 3), (L.right = 3, R.left = null) |
| (null, 3) | exactly one is null | stop, return false |
Complexity. Time O(n). Space O(w), where w is the maximum width, because the queue holds about one level of pairs at a time. Using a stack instead of a queue would also work and would use O(h) space instead; the order in which pairs are checked does not matter, only that every pair is checked.
Edge cases and pitfalls
- Empty tree and single node. Both are symmetric. Approaches 2 and 3 handle the empty tree explicitly; a single node gives the pair
(null, null). - Values alone are not enough. Checking that each level’s values form a palindrome fails on Example 2 unless you also record the
nullgaps. The tests include such a tree, plus[1, 2, 2, 2, null, 2], whose levels also look like palindromes. - Pairing the wrong children. Comparing
a.leftwithb.leftchecks whether the halves are identical, not mirrored. The outer pair is(a.left, b.right)and the inner pair is(a.right, b.left). - Order of the null checks. Read
a.valonly after confirming both nodes are non-null. - Negative values and duplicates. Values are compared with
==onint, so negatives need no special care. Equal values are expected in a symmetric tree. - A single different leaf. The large test builds a perfect symmetric tree, verifies
true, then changes one deepest leaf and verifiesfalse.
Related problems
- Symmetric Binary Tree is the same problem in the A2Z sheet.
- Check if two trees are identical or not is the same recursion with the pairs not swapped.
- Invert/Flip Binary Tree (Create) builds the mirror image used in Approach 1.
- Subtree of Another Tree also compares two trees node by node.
- Level Order Traversal is the BFS pattern behind Approach 3.