2331. Evaluate Boolean Binary Tree - Explanation

Problem Link

Description

Evaluate the expression represented by a full binary tree and return its Boolean result. Every node has either no children or exactly two children. A leaf value of 0 means false, and 1 means true. An internal value of 2 applies OR to its children; an internal value of 3 applies AND.

Examples use level-order arrays. After the root, each present node contributes left and right child entries in queue order. null marks a missing child, and trailing missing entries may be omitted.

Example 1:

Evaluate Boolean Binary Tree, example 1: illustration of the example

Input: root = [3,1,2,null,null,0,1]

Output: true

Explanation: The right subtree computes false OR true, which is true. The root then computes true AND true, giving true.

Example 2:

Evaluate Boolean Binary Tree, example 2: illustration of the example

Input: root = [2,0,3,null,null,1,0]

Output: false

Explanation: The right subtree computes true AND false, which is false. The root computes false OR false, giving false.

Example 3:

Evaluate Boolean Binary Tree, example 3: illustration of the example

Input: root = [1]

Output: true

Explanation: The root is a leaf with value 1, so the expression is true.

Constraints:

  • The tree contains between 1 and 1000 nodes.
  • Every node has either zero or two children.
  • Leaves contain 0 or 1. Internal nodes contain 2 or 3.


Topics

Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Binary Tree Traversal - Understanding how to navigate tree structures using left and right children
  • Recursion - Using recursive calls to evaluate subtrees before combining results at parent nodes
  • Post-order Traversal - Evaluating children before the parent, which is essential for expression tree evaluation
  • Boolean Logic - Understanding AND/OR operations and how to combine boolean values

Intuition

This is a classic tree evaluation problem. Leaf nodes hold boolean values (0 or 1), while internal nodes represent OR (2) or AND (3) operations. We recursively evaluate each subtree: leaf nodes return their value directly, and internal nodes combine their children's results using the appropriate operator. The tree structure naturally maps to the recursive call stack.

Algorithm

  1. If the node has no left child (it's a leaf), return true if val == 1, else false.
  2. Recursively evaluate the left and right subtrees.
  3. If val == 2 (OR), return left_result OR right_result.
  4. If val == 3 (AND), return left_result AND right_result.
# 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 evaluateTree(self, root: Optional[TreeNode]) -> bool:
        if not root.left:
            return root.val == 1

        if root.val == 2:
            return self.evaluateTree(root.left) or self.evaluateTree(root.right)

        if root.val == 3:
            return self.evaluateTree(root.left) and self.evaluateTree(root.right)

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(n)O(n) for recursion stack.

2. Iterative DFS

Intuition

We can avoid recursion by using an explicit stack and a hash map to store computed values. The key challenge is handling the post-order nature of evaluation: a node can only be evaluated after both its children are processed. We achieve this by pushing nodes back onto the stack if their children haven't been evaluated yet, effectively simulating the recursive call stack.

Algorithm

  1. Initialize a stack with the root and a hash map value to store results.
  2. While the stack is non-empty:
    • Pop a node.
    • If it's a leaf, store its boolean value in value.
    • If both children are already in value, compute and store the result.
    • Otherwise, push the node back, then push both children (so they get processed first).
  3. Return value[root].
# 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 evaluateTree(self, root: Optional[TreeNode]) -> bool:
        stack = [root]
        value = {} # map (node -> value)

        while stack:
            node = stack.pop()

            if not node.left:
                value[node] = node.val == 1
            elif node.left in value:
                if node.val == 2:
                    value[node] = value[node.left] or value[node.right]
                if node.val == 3:
                    value[node] = value[node.left] and value[node.right]
            else:
                stack.extend([node, node.left, node.right])

        return value[root]

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(n)O(n)

Common Pitfalls

Confusing Leaf Detection with Null Check

To identify a leaf node, check if root.left is null (since the problem guarantees full binary tree, both children are null or both exist). A common mistake is checking root == null instead, which would return incorrect results for the recursive base case.

Mixing Up Operator Values

The problem uses val == 2 for OR and val == 3 for AND. Swapping these operators or using incorrect comparison values produces wrong boolean results. Always double-check the operator mapping: 0/1 for leaf values, 2 for OR, 3 for AND.