Evaluate Boolean Binary Tree

Updated on 23 May, 2025
Evaluate Boolean Binary Tree header image

Problem Statement

In this task, you are given a full binary tree, characterized by nodes that represent either boolean values or operators. Specifically, the leaf nodes in this tree have values of 0 or 1, corresponding to boolean False and True respectively. Non-leaf nodes, which are the intermediate nodes between the root and the leaves or other non-leaf nodes, have values of 2 or 3, representing the boolean operations "OR" and "AND". The challenge is to evaluate the boolean result starting from the root node by applying the respective boolean operations as described by the values in non-leaf nodes. The end result will be either True or False.

A full binary tree is defined as one where every node has either none or exactly two children. The process involves evaluating from the bottom (leaf nodes) up to the root by consecutively applying the designated operations (OR or AND) as defined by the non-leaf nodes' values.

Examples

Example 1

Input:

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

Output:

true

Explanation:

The above diagram illustrates the evaluation process.
The AND node evaluates to False AND True = False.
The OR node evaluates to True OR False = True.
The root node evaluates to True, so we return true.

Example 2

Input:

root = [0]

Output:

false

Explanation:

The root node is a leaf node and it evaluates to false, so we return false.

Constraints

  • The number of nodes in the tree is in the range [1, 1000].
  • 0 <= Node.val <= 3
  • Every node has either 0 or 2 children.
  • Leaf nodes have a value of 0 or 1.
  • Non-leaf nodes have a value of 2 or 3.

Approach and Intuition

The evaluation of the binary tree from root follows a depth-first approach because each node's result depends on the results of its child nodes. The task can be broken down as follows:

  1. Visit the child nodes first to gather the values from the leaf nodes or evaluate lower level non-leaf nodes.
  2. Apply the boolean operation corresponding to the current node's value:
    • For a leaf node (values 0 or 1), directly take the boolean equivalent (False for 0 and True for 1).
    • For a non-leaf node:
      • If the node value is 2 (OR), evaluate using logical OR between the results of the two child nodes.
      • If the node value is 3 (AND), evaluate using logical AND between the results of the two child nodes.
  3. Propagate the result up to the parent node and repeat the operation until the root node is evaluated.
  4. The final value of the root node after applying all the operations from its descendants is the result of the entire tree evaluation.

Each node effectively processes the results of its child nodes through its assigned operation, leading to a cascading effect of evaluations that concludes at the root node. This approach ensures that by the time we are evaluating a non-leaf node, its children (whether direct leaves or sub-trees themselves) are fully evaluated.

Solutions

  • C++
  • Java
  • Python
cpp
class Solution {
public:
    bool evaluateTree(TreeNode* node) {
        stack<TreeNode*> nodeStack;
        nodeStack.push(node);
        unordered_map<TreeNode*, bool> results;

        while (!nodeStack.empty()) {
            TreeNode* currentNode = nodeStack.top();

            if (!currentNode->left && !currentNode->right) {
                nodeStack.pop();
                results[currentNode] = currentNode->val;
                continue;
            }

            if (results.find(currentNode->left) != results.end() &&
                results.find(currentNode->right) != results.end()) {
                nodeStack.pop();
                if (currentNode->val == 2) {
                    results[currentNode] =
                        results[currentNode->left] || results[currentNode->right];
                } else {
                    results[currentNode] =
                        results[currentNode->left] && results[currentNode->right];
                }
            } else {
                if (currentNode->left) nodeStack.push(currentNode->left);
                if (currentNode->right) nodeStack.push(currentNode->right);
            }
        }

        return results[node] == 1;
    }
};

This solution evaluates a binary tree to determine the truth value of expressions represented by the nodes. In this tree, leaf nodes denote boolean values, and internal nodes denote boolean operations (AND represented by '1', OR by '2'). The code uses the C++ language.

Detailed Breakdown:

  1. The evaluateTree function uses a stack (nodeStack) to traverse nodes in the tree and an unordered map (results) to store evaluation results of the nodes.

  2. The traversal begins from the root node, adding it to the stack. The code then enters a while loop that processes nodes until the stack is empty.

  3. Inside the loop:

    • The top node from the stack (currentNode) is examined.
    • If currentNode is a leaf node (both left and right children are null), its boolean value (currentNode->val) is directly stored in the results map, and the node is removed from the stack.
    • If currentNode is an internal node:
      • Once both its children are evaluated and present in the results map, it calculates the logical operation based on the type of the node (currentNode->val):
        • For '2' (OR operation), combine the results of left and right children using logical OR.
        • For '1' (AND operation), combine them using logical AND.
      • The combined result is stored back in the results map.
    • If the current node's children are not yet processed, they are added to the stack for subsequent evaluation.
  4. At the end of the loop, the function returns the evaluation result of the root node by checking if results[node] == 1.

  • This approach effectively mimics a post-order traversal using a manual stack to manage tree nodes, ensuring that child nodes are processed before their parent nodes, which is crucial for evaluating the expressions represented by the tree.
java
class Solution {
    public boolean evaluateTree(TreeNode node) {
        Stack<TreeNode> stack = new Stack<>();
        stack.push(node);
        HashMap<TreeNode, Boolean> values = new HashMap<>();

        while (!stack.isEmpty()) {
            TreeNode currentNode = stack.peek();
            if (currentNode.left == null && currentNode.right == null) {
                stack.pop();
                values.put(currentNode, currentNode.val == 1);
                continue;
            }

            if (values.containsKey(currentNode.left) && values.containsKey(currentNode.right)) {
                stack.pop();
                if (currentNode.val == 2) {
                    values.put(currentNode, values.get(currentNode.left) || values.get(currentNode.right));
                } else {
                    values.put(currentNode, values.get(currentNode.left) && values.get(currentNode.right));
                }
            } else {
                if (currentNode.right != null) {
                    stack.push(currentNode.right);
                }
                if (currentNode.left != null) {
                    stack.push(currentNode.left);
                }
            }
        }
        
        return values.get(node);
    }
}

The provided Java solution implements a method to evaluate a boolean binary tree, where each leaf node represents a boolean value (0 for false, 1 for true), and the internal nodes represent logical operations (AND represented by 3, OR by 2). The method uses a depth-first search approach utilizing a stack to traverse the tree and a hashmap to store the evaluated values of the nodes.

  • The method evaluateTree(TreeNode node) defines the root of the tree with which the evaluation starts.
  • A Stack<TreeNode> is initiated to manage the nodes during tree traversal and a HashMap<TreeNode, Boolean> to store the truth values of nodes.
  • Within a loop, nodes are popped from the stack, evaluated based on their type (operator or operand), and their resultant values are stored in the hashmap.
  • For internal nodes (nodes representing AND or OR), results from their left and right children are fetched from the hashmap, and the boolean operation respective to the value of the current node is applied.
  • The loop continues until all nodes are evaluated, and the final boolean result stored in the hashmap for the root node is returned.

This depth-first evaluation avoids recursion and effectively calculates the boolean result for a binary tree representing a logical expression, making it a robust solution for such types of problems.

python
class Solution:
    def processNode(self, node: Optional[TreeNode]) -> bool:
        nodes = [node]
        results = {}
        
        while nodes:
            current = nodes[-1]
            
            if not current.left and not current.right:
                nodes.pop()
                results[current] = current.val == 1
                continue
            
            if current.left in results and current.right in results:
                nodes.pop()
                if current.val == 2:
                    results[current] = results[current.left] or results[current.right]
                else:
                    results[current] = results[current.left] and results[current.right]
            else:
                if current.left:
                    nodes.append(current.left)
                if current.right:
                    nodes.append(current.right)
        
        return results[node]

The given Python code defines a method processNode within the Solution class, which evaluates a boolean binary tree based on certain logical operations represented by the node values. The tree nodes can represent either boolean values (True or False, depicted as 1 or 0) or boolean operators (AND and OR, depicted as 3 and 2 correspondingly).

The approach employs a depth-first search (DFS) strategy with the help of a list nodes that acts as a stack to store nodes to be processed. A dictionary results is used to map each node to its computed boolean value.

  • Key steps in the code include:

    1. Initializing the stack with the root node and an empty dictionary for storing results.
    2. Iterating until there are no nodes left in the stack.
    3. Checking if the current node is a leaf by verifying it has no children; the result for this node is directly assigned based on its value.
    4. Determining the result for non-leaf nodes by checking if the results for both child nodes are already computed:
      • For nodes with value 2 (OR operation), the result is the logical OR of both children's results.
      • For nodes with value 3 (AND operation), the result is the logical AND of both children's results.
    5. If results of child nodes are not computed yet, children nodes are added to the stack for further processing.
  • The method finally returns the computed result of the root node.

This structure makes the function robust and efficient for evaluating trees representing complex boolean expressions. The use of depth-first logic ensures that all nodes are processed in the correct order, achieving appropriate boolean evaluation based on the pre-defined logical rules in the tree structure.

Comments

No comments yet.