Forgetting null/base-case handling
Wrong move: Recursive traversal assumes children always exist.
Usually fails on: Leaf nodes throw errors or create wrong depth/path values.
Fix: Handle null/base cases before recursive transitions.
Move from brute-force thinking to an efficient approach using tree strategy.
For a binary tree T, we can define a flip operation as follows: choose any node, and swap the left and right child subtrees.
A binary tree X is flip equivalent to a binary tree Y if and only if we can make X equal to Y after some number of flip operations.
Given the roots of two binary trees root1 and root2, return true if the two trees are flip equivalent or false otherwise.
Example 1:
Input: root1 = [1,2,3,4,5,6,null,null,null,7,8], root2 = [1,3,2,null,6,4,5,null,null,null,null,8,7] Output: true Explanation: We flipped at nodes with values 1, 3, and 5.
Example 2:
Input: root1 = [], root2 = [] Output: true
Example 3:
Input: root1 = [], root2 = [1] Output: false
Constraints:
[0, 100].[0, 99].Problem summary: For a binary tree T, we can define a flip operation as follows: choose any node, and swap the left and right child subtrees. A binary tree X is flip equivalent to a binary tree Y if and only if we can make X equal to Y after some number of flip operations. Given the roots of two binary trees root1 and root2, return true if the two trees are flip equivalent or false otherwise.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Tree
[1,2,3,4,5,6,null,null,null,7,8] [1,3,2,null,6,4,5,null,null,null,null,8,7]
[] []
[] [1]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #951: Flip Equivalent Binary Trees
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean flipEquiv(TreeNode root1, TreeNode root2) {
return dfs(root1, root2);
}
private boolean dfs(TreeNode root1, TreeNode root2) {
if (root1 == root2 || (root1 == null && root2 == null)) {
return true;
}
if (root1 == null || root2 == null || root1.val != root2.val) {
return false;
}
return (dfs(root1.left, root2.left) && dfs(root1.right, root2.right))
|| (dfs(root1.left, root2.right) && dfs(root1.right, root2.left));
}
}
// Accepted solution for LeetCode #951: Flip Equivalent Binary Trees
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func flipEquiv(root1 *TreeNode, root2 *TreeNode) bool {
var dfs func(root1, root2 *TreeNode) bool
dfs = func(root1, root2 *TreeNode) bool {
if root1 == root2 || (root1 == nil && root2 == nil) {
return true
}
if root1 == nil || root2 == nil || root1.Val != root2.Val {
return false
}
return (dfs(root1.Left, root2.Left) && dfs(root1.Right, root2.Right)) || (dfs(root1.Left, root2.Right) && dfs(root1.Right, root2.Left))
}
return dfs(root1, root2)
}
# Accepted solution for LeetCode #951: Flip Equivalent Binary Trees
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def flipEquiv(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> bool:
def dfs(root1, root2):
if root1 == root2 or (root1 is None and root2 is None):
return True
if root1 is None or root2 is None or root1.val != root2.val:
return False
return (dfs(root1.left, root2.left) and dfs(root1.right, root2.right)) or (
dfs(root1.left, root2.right) and dfs(root1.right, root2.left)
)
return dfs(root1, root2)
// Accepted solution for LeetCode #951: Flip Equivalent Binary Trees
struct Solution;
use rustgym_util::*;
trait FlipEq {
fn flip_eq(root1: &TreeLink, root2: &TreeLink) -> bool;
}
impl FlipEq for TreeLink {
fn flip_eq(root1: &TreeLink, root2: &TreeLink) -> bool {
if let (Some(node1), Some(node2)) = (root1, root2) {
let val1 = node1.borrow().val;
let val2 = node2.borrow().val;
let left1 = &node1.borrow().left;
let right1 = &node1.borrow().right;
let left2 = &node2.borrow().left;
let right2 = &node2.borrow().right;
val1 == val2
&& (Self::flip_eq(left1, left2) && Self::flip_eq(right1, right2)
|| Self::flip_eq(left1, right2) && Self::flip_eq(right1, left2))
} else {
root1 == root2
}
}
}
impl Solution {
fn flip_equiv(root1: TreeLink, root2: TreeLink) -> bool {
TreeLink::flip_eq(&root1, &root2)
}
}
#[test]
fn test() {
let root1 = tree!(
1,
tree!(2, tree!(4), tree!(5, tree!(7), tree!(8))),
tree!(3, tree!(6), None)
);
let root2 = tree!(
1,
tree!(3, None, tree!(6)),
tree!(2, tree!(4), tree!(5, tree!(8), tree!(7)))
);
let res = true;
assert_eq!(Solution::flip_equiv(root1, root2), res);
let root1 = tree!(0, tree!(3), tree!(1, None, tree!(2)));
let root2 = tree!(0, tree!(3, tree!(2), None), tree!(1));
let res = false;
assert_eq!(Solution::flip_equiv(root1, root2), res);
}
// Accepted solution for LeetCode #951: Flip Equivalent Binary Trees
function flipEquiv(root1: TreeNode | null, root2: TreeNode | null): boolean {
if (root1 === root2) return true;
if (!root1 || !root2 || root1?.val !== root2?.val) return false;
const { left: l1, right: r1 } = root1!;
const { left: l2, right: r2 } = root2!;
return (flipEquiv(l1, l2) && flipEquiv(r1, r2)) || (flipEquiv(l1, r2) && flipEquiv(r1, l2));
}
Use this to step through a reusable interview workflow for this problem.
BFS with a queue visits every node exactly once — O(n) time. The queue may hold an entire level of the tree, which for a complete binary tree is up to n/2 nodes = O(n) space. This is optimal in time but costly in space for wide trees.
Every node is visited exactly once, giving O(n) time. Space depends on tree shape: O(h) for recursive DFS (stack depth = height h), or O(w) for BFS (queue width = widest level). For balanced trees h = log n; for skewed trees h = n.
Review these before coding to avoid predictable interview regressions.
Wrong move: Recursive traversal assumes children always exist.
Usually fails on: Leaf nodes throw errors or create wrong depth/path values.
Fix: Handle null/base cases before recursive transitions.