Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

951. Flip Equivalent Binary Trees - Leetcode Solution

Code Implementation

# 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: TreeNode, root2: TreeNode) -> bool:
        if not root1 and not root2:
            return True
        if not root1 or not root2 or root1.val != root2.val:
            return False
        return (
            (self.flipEquiv(root1.left, root2.left) and self.flipEquiv(root1.right, root2.right)) or
            (self.flipEquiv(root1.left, root2.right) and self.flipEquiv(root1.right, root2.left))
        )
      
// Definition for a binary tree node.
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

class Solution {
public:
    bool flipEquiv(TreeNode* root1, TreeNode* root2) {
        if (!root1 && !root2) return true;
        if (!root1 || !root2 || root1->val != root2->val) return false;
        return (flipEquiv(root1->left, root2->left) && flipEquiv(root1->right, root2->right)) ||
               (flipEquiv(root1->left, root2->right) && flipEquiv(root1->right, root2->left));
    }
};
      
// Definition for a binary tree node.
public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

class Solution {
    public boolean flipEquiv(TreeNode root1, TreeNode root2) {
        if (root1 == null && root2 == null) return true;
        if (root1 == null || root2 == null || root1.val != root2.val) return false;
        return (flipEquiv(root1.left, root2.left) && flipEquiv(root1.right, root2.right)) ||
               (flipEquiv(root1.left, root2.right) && flipEquiv(root1.right, root2.left));
    }
}
      
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root1
 * @param {TreeNode} root2
 * @return {boolean}
 */
var flipEquiv = function(root1, root2) {
    if (!root1 && !root2) return true;
    if (!root1 || !root2 || root1.val !== root2.val) return false;
    return (
        (flipEquiv(root1.left, root2.left) && flipEquiv(root1.right, root2.right)) ||
        (flipEquiv(root1.left, root2.right) && flipEquiv(root1.right, root2.left))
    );
};
      

Problem Description

Given two binary trees represented by their root nodes root1 and root2, determine if they are flip equivalent. Two binary trees are flip equivalent if they are the same when you are allowed to flip (swap) the left and right children of any number of nodes in either tree.

  • You may flip any number of nodes, and you may choose to flip different nodes in each tree.
  • The structure and node values must match after any sequence of flips.
  • Both trees may be empty (i.e., the root is null).
  • Node values are not necessarily unique.

The goal is to return True if the trees are flip equivalent, and False otherwise.

Thought Process

At first, you might think to compare the two trees node by node, checking if the left and right children match. However, because we can flip any subtree, a simple traversal won't work. Instead, we need to consider both the original and the flipped configuration at each node.

The key insight is that for two nodes to be flip equivalent, their values must match, and their children must be flip equivalent in either the original or flipped order. This means that at each step, we need to check both possible pairings of children.

A brute-force approach would try all possible sequences of flips, but this quickly becomes infeasible as the tree grows. Instead, we use recursion to check both possibilities at each node, which is much more efficient and elegant.

Solution Approach

  • Step 1: If both nodes are null, they are trivially equivalent. Return True.
  • Step 2: If only one node is null, or their values differ, they can't be equivalent. Return False.
  • Step 3: Otherwise, recursively check two possibilities:
    • The left child of root1 is flip equivalent to the left child of root2 and the right child of root1 is flip equivalent to the right child of root2 (no flip at this node).
    • The left child of root1 is flip equivalent to the right child of root2 and the right child of root1 is flip equivalent to the left child of root2 (flipped at this node).
  • Step 4: If either possibility is true, the subtrees rooted at these nodes are flip equivalent.

This recursive approach naturally explores all valid flip combinations without redundant work, since at each node we only consider two configurations.

Example Walkthrough

Let's look at an example:

    Tree 1:         1
                  /   \
                 2     3
                /     / \
               4     5   6

    Tree 2:         1
                  /   \
                 3     2
                / \     \
               6   5     4
  
  1. Start at the root: both have value 1. Proceed to children.
  2. Tree 1's left child is 2, right child is 3. Tree 2's left child is 3, right child is 2. The children are swapped, so we consider the flipped configuration.
  3. Check if 2 (Tree 1 left) is flip equivalent to 2 (Tree 2 right): both have value 2.
  4. Tree 1's 2 has left child 4, Tree 2's 2 has right child 4. Again, they are swapped, so flip. Both 4s are leaves, so return True.
  5. Tree 1's 2 has no right child, Tree 2's 2 has no left child. Both null, return True.
  6. Now check the other side: 3 (Tree 1 right) vs 3 (Tree 2 left). Both have value 3.
  7. Tree 1's 3 has left child 5, right child 6. Tree 2's 3 has left child 6, right child 5. Again, swapped, so flip. 5 vs 5 and 6 vs 6, both leaves, return True.
  8. All checks return True, so the trees are flip equivalent.

Time and Space Complexity

  • Brute-force Approach: If you tried all possible flip combinations, the time complexity could be exponential, as each node offers a flip/no-flip choice.
  • Optimized Recursive Approach: Each node is visited once, and at each node, we do constant work (checking values and making two recursive calls). So, the time complexity is O(N), where N is the number of nodes in the trees.
  • Space Complexity: The space used is O(H) for the recursion stack, where H is the height of the tree. In the worst case (a skewed tree), H = N.

Summary

The flip equivalent binary trees problem asks whether two trees can be made identical by flipping any number of left and right children. The elegant recursive solution checks, at each node, both the original and flipped child pairings. This approach is efficient, with linear time complexity, and avoids the combinatorial explosion of brute-force methods. The key insight is that flip equivalence is a local property at each pair of nodes, making recursion a natural fit for the problem.