Problem
Given the roots of two binary trees p and q, determine whether they are exactly the same.
Two binary trees are considered the same if:
- They have the same structure
- Every corresponding node contains the same value
For example:
Input:
p = [1,2,3]
q = [1,2,3]
Output:
trueInput:
p = [4,7]
q = [4,null,7]
Output:
falseAlthough both trees contain the values 4 and 7, their structures differ, so they are not the same.
Intuition
At every pair of nodes, we need to answer three questions:
- Are both nodes missing?
- Is one node missing while the other exists?
- If both exist, do they have the same value?
If the current pair of nodes passes these checks, we simply repeat the exact same process for their children.
Notice that this problem naturally breaks itself into two identical subproblems:
- Compare the left subtrees.
- Compare the right subtrees.
This makes recursion a perfect fit.
Recursive DFS Solution
from typing import Optional
class Solution:
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
if not p and not q:
return True
if not p or not q:
return False
if p.val != q.val:
return False
return (
self.isSameTree(p.left, q.left)
and
self.isSameTree(p.right, q.right)
)Step-by-Step
Suppose we compare these trees:
1 1
/ \ / \
2 3 2 3We begin at the roots.
1 == 1 ✓Now compare the left children.
2 == 2 ✓Both left children have no children, so every recursive call returns True.
Next compare the right children.
3 == 3 ✓Again, both children are identical.
Since both the left comparison and the right comparison return True, the original call also returns True.
Now consider:
1 1
/ \
2 2The first comparison succeeds.
1 == 1 ✓Then we compare the left children.
2 vs NoneOne node exists while the other doesn't.
Immediately we return:
FalseThere's no need to continue checking the rest of the tree.
Why the Base Cases Matter
Case 1: Both nodes are None
if not p and not q:
return TrueIf we've reached the end of both trees simultaneously, everything matched up to this point.
None NoneThese are considered equal.
Case 2: Only one node exists
if not p or not q:
return FalseExamples:
2 Noneor
None 2The structures differ, so the trees cannot be identical.
Case 3: Values differ
if p.val != q.val:
return FalseExample:
5 8Even though the structure matches, the node values do not.
Why We Use and
The trees are only identical if:
- the left subtrees are identical
- and
- the right subtrees are identical
return (
self.isSameTree(p.left, q.left)
and
self.isSameTree(p.right, q.right)
)Think of it like this:
Same Tree?
Left matches? True
Right matches? True
Result = True AND True
= TrueBut if either side fails:
Left matches? False
Right matches? True
Result = FalseOne mismatch anywhere in the tree means the trees are different.
A Slightly Shorter Version
The same logic can be written a little more compactly:
class Solution:
def isSameTree(self, p, q):
if not p and not q:
return True
if p and q and p.val == q.val:
return (
self.isSameTree(p.left, q.left)
and
self.isSameTree(p.right, q.right)
)
return FalseThis combines several checks into a single condition:
- Both nodes exist.
- Their values match.
Otherwise, return False.
Functionally, it is identical to the previous solution.
Complexity Analysis
Time Complexity
O(n)Every pair of corresponding nodes is visited exactly once.
Space Complexity
O(h)Where h is the height of the tree due to the recursive call stack.
- Balanced tree:
O(log n) - Skewed tree:
O(n)
Interview Expectations
For this problem, the recursive DFS solution is the standard interview solution.
Interviewers are mainly looking for whether you correctly handle the three key cases:
- Both nodes are
None. - One node is
None. - Both nodes exist but their values differ.
Once those cases are handled, recursively comparing the left and right subtrees is straightforward.
An iterative solution using a queue or stack is also valid and demonstrates familiarity with tree traversals, but it is not generally expected unless the interviewer specifically asks for a non-recursive approach.
Key Takeaways
- Compare two nodes at a time.
- Handle the three base cases before recursing.
- Both left and right subtrees must match.
- Recursion mirrors the recursive structure of a binary tree, making it the cleanest solution.
- The recursive DFS solution runs in O(n) time and O(h) space, which is optimal for this problem.