Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1597. Build Binary Expression Tree From Infix Expression - Leetcode Solution

Problem Description

The "Build Binary Expression Tree From Infix Expression" problem asks you to construct a binary expression tree from a given infix mathematical expression. An infix expression is a string containing numbers, operators (+, -, *, /), and parentheses, written in the usual way (e.g., 2*(3+4)).

The goal is to parse this string and build a binary tree where:

  • Each internal node represents an operator.
  • Each leaf node represents an operand (number).
  • The tree structure reflects the precedence and grouping of the operations as in the original infix expression.
The output should be the root of the constructed binary expression tree.

The problem guarantees that the input expression is valid and contains only non-negative integers, the operators +, -, *, /, and parentheses ( ). There is exactly one valid way to parse the expression, and you should not reuse or rearrange elements.

Thought Process

To solve this problem, we need to convert an infix expression (which is how we usually write math) into a binary tree that represents the order of operations. The key challenge is respecting operator precedence (e.g., multiplication before addition) and grouping via parentheses.

A brute-force approach might try to recursively split the string at every possible operator, but this quickly becomes inefficient and complicated, especially with nested parentheses and operator precedence.

Instead, we can use a stack-based algorithm, much like the Shunting Yard algorithm or the process used to convert infix expressions to postfix notation. By using two stacks—one for operators and one for tree nodes—we can process the expression from left to right, building up the tree as we go. This approach efficiently handles precedence and grouping, and is well-suited for constructing the binary expression tree.

Solution Approach

Let's break down the solution step by step:

  1. Tokenization:
    • We scan the input string character by character, grouping consecutive digits into multi-digit numbers.
  2. Use Two Stacks:
    • One stack for operators (op_stack).
    • One stack for tree nodes (node_stack).
  3. Processing the Expression:
    • If we see a number, create a leaf node and push it onto node_stack.
    • If we see an operator, pop operators from op_stack with higher or equal precedence and build subtrees, then push the current operator.
    • If we see an open parenthesis (, push it onto op_stack.
    • If we see a close parenthesis ), pop operators and build subtrees until we find the matching (.
  4. Final Assembly:
    • After processing the entire string, pop any remaining operators and build the final subtrees.
  5. Return the Root:
    • The last node on node_stack is the root of the binary expression tree.

This method ensures that the tree structure correctly reflects operator precedence and parentheses, just like how a calculator would evaluate the expression.

Example Walkthrough

Let's walk through the process with the example input: 2*(3+4)

  1. Read 2: Push a leaf node with value 2 onto node_stack.
  2. Read *: Push * onto op_stack.
  3. Read (: Push ( onto op_stack.
  4. Read 3: Push a leaf node with value 3 onto node_stack.
  5. Read +: Push + onto op_stack (since ( is on top, no popping).
  6. Read 4: Push a leaf node with value 4 onto node_stack.
  7. Read ):
    • Pop + from op_stack, pop two nodes (3 and 4), build + node with these as children, push onto node_stack.
    • Pop ( from op_stack.
  8. End of input: Pop * from op_stack, pop two nodes (2 and (3+4)), build * node, push onto node_stack.
  9. The root node is *, with left child 2 and right child + (which has children 3 and 4).

The resulting tree structure is:

  • *
    • Left: 2
    • Right: +
      • Left: 3
      • Right: 4

Time and Space Complexity

Brute-force approach: If you tried every possible way to parenthesize or split the expression, the time complexity would be exponential, as the number of ways to fully parenthesize grows rapidly with input size.

Optimized stack-based approach:

  • Time Complexity: O(N), where N is the length of the input string. Each character is processed a constant number of times (pushed/popped from stacks).
  • Space Complexity: O(N), for the stacks and the tree nodes.
This efficiency comes from processing each token only as needed and never revisiting parts of the expression.

Summary

To build a binary expression tree from an infix string, we use a stack-based algorithm inspired by the Shunting Yard method. By carefully managing operator precedence and parentheses with two stacks, we efficiently construct the tree in a single pass. This approach is both elegant and efficient, handling all valid infix expressions robustly.

Code Implementation

# Definition for a binary tree node.
class Node:
    def __init__(self, val, left=None, right=None):
        self.val = val  # operator or operand (str)
        self.left = left
        self.right = right

class Solution:
    def buildTree(self, s: str) -> 'Node':
        def precedence(op):
            if op in ('+', '-'):
                return 1
            if op in ('*', '/'):
                return 2
            return 0

        op_stack = []
        node_stack = []
        i = 0
        n = len(s)

        while i < n:
            if s[i] == ' ':
                i += 1
                continue
            if s[i] == '(':
                op_stack.append('(')
                i += 1
            elif s[i].isdigit():
                num = ''
                while i < n and s[i].isdigit():
                    num += s[i]
                    i += 1
                node_stack.append(Node(num))
            elif s[i] == ')':
                while op_stack and op_stack[-1] != '(':
                    op = op_stack.pop()
                    right = node_stack.pop()
                    left = node_stack.pop()
                    node_stack.append(Node(op, left, right))
                op_stack.pop()  # pop '('
                i += 1
            else:  # operator
                while (op_stack and op_stack[-1] != '(' and
                       precedence(op_stack[-1]) >= precedence(s[i])):
                    op = op_stack.pop()
                    right = node_stack.pop()
                    left = node_stack.pop()
                    node_stack.append(Node(op, left, right))
                op_stack.append(s[i])
                i += 1

        while op_stack:
            op = op_stack.pop()
            right = node_stack.pop()
            left = node_stack.pop()
            node_stack.append(Node(op, left, right))

        return node_stack[-1]
      
/**
 * Definition for a binary tree node.
 * struct Node {
 *     string val;
 *     Node *left;
 *     Node *right;
 *     Node(string x) : val(x), left(NULL), right(NULL) {}
 *     Node(string x, Node* l, Node* r) : val(x), left(l), right(r) {}
 * };
 */
class Solution {
public:
    int precedence(char op) {
        if (op == '+' || op == '-') return 1;
        if (op == '*' || op == '/') return 2;
        return 0;
    }
    Node* buildTree(string s) {
        vector<char> op_stack;
        vector<Node*> node_stack;
        int n = s.size();
        int i = 0;
        while (i < n) {
            if (s[i] == ' ') {
                i++;
                continue;
            }
            if (s[i] == '(') {
                op_stack.push_back('(');
                i++;
            } else if (isdigit(s[i])) {
                string num = "";
                while (i < n && isdigit(s[i])) {
                    num += s[i++];
                }
                node_stack.push_back(new Node(num));
            } else if (s[i] == ')') {
                while (!op_stack.empty() && op_stack.back() != '(') {
                    char op = op_stack.back(); op_stack.pop_back();
                    Node* right = node_stack.back(); node_stack.pop_back();
                    Node* left = node_stack.back(); node_stack.pop_back();
                    node_stack.push_back(new Node(string(1, op), left, right));
                }
                op_stack.pop_back(); // pop '('
                i++;
            } else { // operator
                while (!op_stack.empty() && op_stack.back() != '(' &&
                    precedence(op_stack.back()) >= precedence(s[i])) {
                    char op = op_stack.back(); op_stack.pop_back();
                    Node* right = node_stack.back(); node_stack.pop_back();
                    Node* left = node_stack.back(); node_stack.pop_back();
                    node_stack.push_back(new Node(string(1, op), left, right));
                }
                op_stack.push_back(s[i]);
                i++;
            }
        }
        while (!op_stack.empty()) {
            char op = op_stack.back(); op_stack.pop_back();
            Node* right = node_stack.back(); node_stack.pop_back();
            Node* left = node_stack.back(); node_stack.pop_back();
            node_stack.push_back(new Node(string(1, op), left, right));
        }
        return node_stack.back();
    }
};
      
/**
 * Definition for a binary tree node.
 * public class Node {
 *     public String val;
 *     public Node left, right;
 *     public Node(String val) { this.val = val; }
 *     public Node(String val, Node left, Node right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    private int precedence(char op) {
        if (op == '+' || op == '-') return 1;
        if (op == '*' || op == '/') return 2;
        return 0;
    }
    public Node buildTree(String s) {
        Stack<Character> opStack = new Stack<>();
        Stack<Node> nodeStack = new Stack<>();
        int n = s.length();
        int i = 0;
        while (i < n) {
            if (s.charAt(i) == ' ') {
                i++;
                continue;
            }
            if (s.charAt(i) == '(') {
                opStack.push('(');
                i++;
            } else if (Character.isDigit(s.charAt(i))) {
                StringBuilder num = new StringBuilder();
                while (i < n && Character.isDigit(s.charAt(i))) {
                    num.append(s.charAt(i++));
                }
                nodeStack.push(new Node(num.toString()));
            } else if (s.charAt(i) == ')') {
                while (!opStack.isEmpty() && opStack.peek() != '(') {
                    char op = opStack.pop();
                    Node right = nodeStack.pop();
                    Node left = nodeStack.pop();
                    nodeStack.push(new Node(String.valueOf(op), left, right));
                }
                opStack.pop(); // pop '('
                i++;
            } else { // operator
                while (!opStack.isEmpty() && opStack.peek() != '(' &&
                       precedence(opStack.peek()) >= precedence(s.charAt(i))) {
                    char op = opStack.pop();
                    Node right = nodeStack.pop();
                    Node left = nodeStack.pop();
                    nodeStack.push(new Node(String.valueOf(op), left, right));
                }
                opStack.push(s.charAt(i));
                i++;
            }
        }
        while (!opStack.isEmpty()) {
            char op = opStack.pop();
            Node right = nodeStack.pop();
            Node left = nodeStack.pop();
            nodeStack.push(new Node(String.valueOf(op), left, right));
        }
        return nodeStack.peek();
    }
}
      
/**
 * Definition for a binary tree node.
 * function Node(val, left, right) {
 *     this.val = val;
 *     this.left = left || null;
 *     this.right = right || null;
 * }
 */
var buildTree = function(s) {
    function precedence(op) {
        if (op === '+' || op === '-') return 1;
        if (op === '*' || op === '/') return 2;
        return 0;
    }
    let opStack = [];
    let nodeStack = [];
    let i = 0, n = s.length;
    while (i < n) {
        if (s[i] === ' ') {
            i++;
            continue;
        }
        if (s[i] === '(') {
            opStack.push('(');
            i++;
        } else if (/\d/.test(s[i])) {
            let num = '';
            while (i < n && /\d/.test(s[i])) {
                num += s[i++];
            }
            nodeStack.push(new Node(num));
        } else if (s[i] === ')') {
            while (opStack.length && opStack[opStack.length-1] !== '(') {
                let op = opStack.pop();
                let right = nodeStack.pop();
                let left = nodeStack.pop();
                nodeStack.push(new Node(op, left, right));
            }
            opStack.pop(); // pop '('
            i++;
        } else { // operator
            while (opStack.length && opStack[opStack.length-1] !== '(' &&
                   precedence(opStack[opStack.length-1]) >= precedence(s[i])) {
                let op = opStack.pop();
                let right = nodeStack.pop();
                let left = nodeStack.pop();
                nodeStack.push(new Node(op, left, right));
            }
            opStack.push(s[i]);
            i++;
        }
    }
    while (opStack.length) {
        let op = opStack.pop();
        let right = nodeStack.pop();
        let left = nodeStack.pop();
        nodeStack.push(new Node(op, left, right));
    }
    return nodeStack[nodeStack.length-1];
};