Problem overview
The method isSymmetric(self, root) receives the root of a binary tree and returns True if the tree is a mirror image of itself around its center, False otherwise. Each node has a val, a left child and a right child, and a missing child is None.
The tree [1,2,2,3,4,4,3] is symmetric: below the root 1 there are two 2s, and the left 2 has children 3 and 4 while the right 2 has children 4 and 3, the same pair in reverse. The tree [1,2,2,null,3,null,3] is not symmetric. Both 2s have only a right child, a 3, and a mirror would need the left 2's child on the left of the right 2 and the other on the right.
Approach: compare two subtrees at once
Comparing a tree with its mirror image means checking that the left side is the reflection of the right side. A reflection swaps left and right at every level, so two nodes mirror each other when:
- both are missing, or both exist and hold the same value, and
- the left child of the first mirrors the right child of the second, and
- the right child of the first mirrors the left child of the second.
This is a recursive definition, so the code is a recursive helper, dfs(root1, root2), which answers "do these two subtrees mirror each other?". To test the whole tree, pass the root as both arguments: the helper then walks one copy leftwards while it walks the other copy rightwards, comparing a left-side node to its mirror position on the right.
The alternative is an iterative version with a queue or a stack that holds pairs of nodes to compare. It follows the same pair rule, and the recursive form is shorter.
Step by step
- Return
self.dfs(root, root)fromisSymmetric. A tree is symmetric when it mirrors itself. - Declare
dfs(self, root1, root2). The two parameters are nodes that should be mirror images of each other. - Both empty: return
True.not root1 and not root2is true when both areNone. Two empty subtrees mirror each other. This is also the base case that stops the recursion below the leaves. - Exactly one empty: return
False. The both-empty case has already returned, sonot root1 or not root2means exactly one node is missing, and the shapes differ. - Different values: return
False. Both nodes exist now, so readingvalis safe. - Check the outer pair.
dfs(root1.left, root2.right)compares the left child of the first node with the right child of the second. If that fails, returnFalseimmediately. - Return the inner pair.
dfs(root1.right, root2.left)is all that remains, so its result is the answer.
The solution in Python
class Solution:
def dfs(self, root1, root2):
if not root1 and not root2:
return True
if not root1 or not root2:
return False
if root1.val != root2.val:
return False
if not self.dfs(root1.left, root2.right):
return False
return self.dfs(root1.right, root2.left)
def isSymmetric(self, root: TreeNode) -> bool:
return self.dfs(root, root)
Python details to notice:
not root1is true whenroot1isNone. This works for tree nodes because an ordinary object is always truthy, unless its class defines__bool__or__len__. A stricter, more explicit test isroot1 is None.- The order of the three checks matters. Reading
root1.valon aNonewould raiseAttributeError: 'NoneType' object has no attribute 'val', and the two earlier checks make sure that never happens. dfsis a method, so it is called asself.dfs(...), and it needsselfas its first parameter.- The method returns the result of the last call directly, instead of writing
if ...: return True else: return False.
Complexity
- Time: O(n), where
nis the number of nodes. Because the root is passed as both arguments, each node is visited twice (once from each side), which is still linear. The function returns early at the first mismatch. - Space: O(h) for the recursion stack, where
his the height of the tree: about log n for a balanced tree and up to n for a tree shaped like a list. Python's default recursion limit is 1000 nested calls. A symmetric tree with up to 1000 nodes is at most about 500 levels deep, and a lopsided tree fails the mirror test within a few calls, so the limit is not a concern for this problem.
Edge cases and common mistakes
- A single node.
[1]is symmetric:dfscompares the node to itself, the values match, and both children pairs areNone. - Same values, different shape.
[1,2,2,3,None,None,3]is symmetric, while[1,2,3]is not, because 2 and 3 differ. Shape and values must both match. - Comparing the wrong children.
dfs(root1.left, root2.left)would test whether a tree equals itself, not whether it mirrors itself. The crossing, left with right, is the whole idea. - Checking only the root's two children. Equal values at one level do not imply symmetry deeper down.
Testing
With trees written in level order, [1,2,2,3,4,4,3] gives True, [1,2,2,None,3,None,3] gives False, [1] gives True and [1,2,3] gives False.
