Check if Two Binary Trees are Mirror Images
Problem and examples
| Tree 1 | Tree 2 | Are Mirror? | Description |
|---|---|---|---|
| [1, 2, 3, 4, 5] | [1, 3, 2, null, null, 5, 4] | true | Tree 2 is the mirror image of Tree 1: left and right children are swapped at every node. |
| [10, 20, 30] | [10, 30, 20] | true | Left and right subtrees are perfectly mirrored. |
| [1, 2, 3] | [1, 2, 3] | false | Both trees have the same structure, but are not mirrors (no left-right reversal). |
| [] | [] | true | Two empty trees are trivially mirrors of each other. |
| [1] | [1] | true | Single-node trees with the same value are mirrors. |
| [1, 2] | [1, null, 2] | true | Tree 1 has a left child; Tree 2 has a right child. Structures mirror each other. |
How it works
Understanding the Problem
We are given two binary trees and asked to determine whether they are mirror images of each other. This means that the structure and values of one tree must exactly reflect those of the other, but in a mirrored way. Specifically, for every node in the first tree, its left child must match the right child of the corresponding node in the second tree, and vice versa.
This problem is common in recursion-based tree problems and tests your ability to compare tree structures and values symmetrically.
Step-by-Step Solution with Example
Step 1: Understand the Mirror Concept
Two trees are mirrors if:
- They are both empty (null)
- Or, their root nodes have equal values, and their left and right subtrees are mirrors of each other in opposite directions
Step 2: Use a Recursive Approach
The simplest and cleanest way to solve this is using recursion. We define a function isMirror(tree1, tree2) that checks:
- If both trees are null, return true
- If only one is null, return false
- If values don't match, return false
- Recursively check
isMirror(tree1.left, tree2.right)andisMirror(tree1.right, tree2.left)
Step 3: Apply the Approach to an Example
Let’s take two trees:
Tree A Tree B
1 1
/ / 2 3 3 2
We compare:
- Roots: 1 == 1 ✅
- Left of A (2) with Right of B (2) ✅
- Right of A (3) with Left of B (3) ✅
All checks pass recursively, so these trees are mirror images.
Step 4: Implement the Code
boolean isMirror(TreeNode t1, TreeNode t2) {
if (t1 == null && t2 == null) return true;
if (t1 == null || t2 == null) return false;
if (t1.val != t2.val) return false;
return isMirror(t1.left, t2.right) && isMirror(t1.right, t2.left);
}
Edge Cases
Case 1: Both Trees Are Empty
Two null trees are mirrors by definition. Return true.
Case 2: One Tree is Empty
One null and one non-null tree can never be mirrors. Return false.
Case 3: Different Root Values
If the roots are not equal, trees can't be mirrors. Check t1.val != t2.val.
Case 4: Matching Roots, Unmatched Subtrees
Even if roots match, if the left and right children don't mirror each other in structure and value, return false.
Case 5: Single Node Trees
If both trees are single nodes with the same value, they are mirrors. If either node has children and the other doesn’t, they’re not mirrors.
Finally
This problem teaches the power of recursion and symmetry. The key idea is to mirror the traversal — compare left of one with right of the other and continue down the tree. Always consider the base cases carefully, and remember to match both structure and value. With these principles, you can confidently identify mirror trees!
- Given two binary trees, if both trees are empty, they are mirrors; if one is empty and the other is not, they are not mirrors.
- Compare the root nodes of both trees. If the values are not equal, the trees are not mirrors.
- Recursively check if the left subtree of the first tree is a mirror of the right subtree of the second tree, and if the right subtree of the first tree is a mirror of the left subtree of the second tree.
- If both recursive checks return true, then the two trees are mirror images of each other.
Implementation and efficiency
C implementation
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;
TreeNode* createNode(int val) {
TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));
node->val = val;
node->left = node->right = NULL;
return node;
}
bool isMirror(TreeNode* t1, TreeNode* t2) {
if (t1 == NULL && t2 == NULL) return true;
if (t1 == NULL || t2 == NULL) return false;
return (t1->val == t2->val) &&
isMirror(t1->left, t2->right) &&
isMirror(t1->right, t2->left);
}
int main() {
TreeNode* tree1 = createNode(1);
tree1->left = createNode(2);
tree1->right = createNode(3);
TreeNode* tree2 = createNode(1);
tree2->left = createNode(3);
tree2->right = createNode(2);
printf("%s\n", isMirror(tree1, tree2) ? "true" : "false");
return 0;
}C++ implementation
#include <iostream>
using namespace std;
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
bool isMirror(TreeNode* t1, TreeNode* t2) {
if (!t1 && !t2) return true;
if (!t1 || !t2) return false;
return (t1->val == t2->val) &&
isMirror(t1->left, t2->right) &&
isMirror(t1->right, t2->left);
}
int main() {
TreeNode* tree1 = new TreeNode(1);
tree1->left = new TreeNode(2);
tree1->right = new TreeNode(3);
TreeNode* tree2 = new TreeNode(1);
tree2->left = new TreeNode(3);
tree2->right = new TreeNode(2);
cout << (isMirror(tree1, tree2) ? "true" : "false") << endl;
return 0;
}Python implementation
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def isMirror(t1, t2):
if not t1 and not t2:
return True
if not t1 or not t2:
return False
return (t1.val == t2.val and
isMirror(t1.left, t2.right) and
isMirror(t1.right, t2.left))
if __name__ == '__main__':
tree1 = TreeNode(1, TreeNode(2), TreeNode(3))
tree2 = TreeNode(1, TreeNode(3), TreeNode(2))
print(isMirror(tree1, tree2))Java implementation
class TreeNode {
int val;
TreeNode left, right;
TreeNode(int x) { val = x; }
}
public class MirrorTrees {
public boolean isMirror(TreeNode t1, TreeNode t2) {
if (t1 == null && t2 == null) return true;
if (t1 == null || t2 == null) return false;
return (t1.val == t2.val) &&
isMirror(t1.left, t2.right) &&
isMirror(t1.right, t2.left);
}
public static void main(String[] args) {
MirrorTrees sol = new MirrorTrees();
TreeNode tree1 = new TreeNode(1);
tree1.left = new TreeNode(2);
tree1.right = new TreeNode(3);
TreeNode tree2 = new TreeNode(1);
tree2.left = new TreeNode(3);
tree2.right = new TreeNode(2);
System.out.println(sol.isMirror(tree1, tree2));
}
}JavaScript implementation
class TreeNode {
constructor(val, left = null, right = null) {
this.val = val;
this.left = left;
this.right = right;
}
}
function isMirror(t1, t2) {
if (!t1 && !t2) return true;
if (!t1 || !t2) return false;
return (t1.val === t2.val) &&
isMirror(t1.left, t2.right) &&
isMirror(t1.right, t2.left);
}
const tree1 = new TreeNode(1, new TreeNode(2), new TreeNode(3));
const tree2 = new TreeNode(1, new TreeNode(3), new TreeNode(2));
console.log(isMirror(tree1, tree2));Go implementation
package main
import (
"fmt"
)
type TreeNode struct {
val int
left *TreeNode
right *TreeNode
}
func isMirror(t1, t2 *TreeNode) bool {
if t1 == nil && t2 == nil {
return true
}
if t1 == nil || t2 == nil {
return false
}
return t1.val == t2.val &&
isMirror(t1.left, t2.right) &&
isMirror(t1.right, t2.left)
}
func main() {
tree1 := &TreeNode{1, &TreeNode{2, nil, nil}, &TreeNode{3, nil, nil}}
tree2 := &TreeNode{1, &TreeNode{3, nil, nil}, &TreeNode{2, nil, nil}}
fmt.Println(isMirror(tree1, tree2))
}Rust implementation
struct TreeNode {
val: i32,
left: Option<Box<TreeNode>>,
right: Option<Box<TreeNode>>,
}
fn is_mirror(t1: &Option<Box<TreeNode>>, t2: &Option<Box<TreeNode>>) -> bool {
match (t1, t2) {
(None, None) => true,
(Some(n1), Some(n2)) => {
n1.val == n2.val &&
is_mirror(&n1.left, &n2.right) &&
is_mirror(&n1.right, &n2.left)
},
_ => false,
}
}
fn main() {
let tree1 = Some(Box::new(TreeNode {
val: 1,
left: Some(Box::new(TreeNode { val: 2, left: None, right: None })),
right: Some(Box::new(TreeNode { val: 3, left: None, right: None })),
}));
let tree2 = Some(Box::new(TreeNode {
val: 1,
left: Some(Box::new(TreeNode { val: 3, left: None, right: None })),
right: Some(Box::new(TreeNode { val: 2, left: None, right: None })),
}));
println!("{}", is_mirror(&tree1, &tree2));
}Kotlin implementation
class TreeNode(val value: Int) {
var left: TreeNode? = null
var right: TreeNode? = null
}
fun isMirror(t1: TreeNode?, t2: TreeNode?): Boolean {
if (t1 == null && t2 == null) return true
if (t1 == null || t2 == null) return false
return (t1.value == t2.value) &&
isMirror(t1.left, t2.right) &&
isMirror(t1.right, t2.left)
}
fun main() {
val tree1 = TreeNode(1)
tree1.left = TreeNode(2)
tree1.right = TreeNode(3)
val tree2 = TreeNode(1)
tree2.left = TreeNode(3)
tree2.right = TreeNode(2)
println(isMirror(tree1, tree2))
}Swift implementation
class TreeNode {
var val: Int
var left: TreeNode?
var right: TreeNode?
init(_ val: Int) {
self.val = val
self.left = nil
self.right = nil
}
}
func isMirror(_ t1: TreeNode?, _ t2: TreeNode?) -> Bool {
if t1 == nil && t2 == nil { return true }
if t1 == nil || t2 == nil { return false }
return t1!.val == t2!.val &&
isMirror(t1!.left, t2!.right) &&
isMirror(t1!.right, t2!.left)
}
let tree1 = TreeNode(1)
tree1.left = TreeNode(2)
tree1.right = TreeNode(3)
let tree2 = TreeNode(1)
tree2.left = TreeNode(3)
tree2.right = TreeNode(2)
print(isMirror(tree1, tree2))TypeScript implementation
class TreeNode {
val: number;
left: TreeNode | null;
right: TreeNode | null;
constructor(val: number, left: TreeNode | null = null, right: TreeNode | null = null) {
this.val = val;
this.left = left;
this.right = right;
}
}
function isMirror(t1: TreeNode | null, t2: TreeNode | null): boolean {
if (!t1 && !t2) return true;
if (!t1 || !t2) return false;
return t1.val === t2.val &&
isMirror(t1.left, t2.right) &&
isMirror(t1.right, t2.left);
}
const tree1 = new TreeNode(1, new TreeNode(2), new TreeNode(3));
const tree2 = new TreeNode(1, new TreeNode(3), new TreeNode(2));
console.log(isMirror(tree1, tree2));