Serialize Expression Tree Minimizing Parentheses
Company: Waymo
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates proficiency with binary expression trees, operator precedence and associativity, and the ability to serialize an abstract syntax tree into an infix string using the minimum necessary parentheses.
Constraints
- 1 <= n == len(types) == len(values) == len(left) == len(right) <= 1000
- Each `types[i]` is either `'var'` or one of `'+', '-', '*', '/'`
- If `types[i] == 'var'`, then `values[i]` is a non-empty identifier and `left[i] = right[i] = -1`
- The input describes a valid binary tree rooted at `root`
Examples
Input: (['var'], ['foo'], [-1], [-1], 0)
Expected Output: 'foo'
Explanation: A single variable leaf needs no parentheses.
Input: (['var', 'var', '+', 'var', '*'], ['a', 'b', '', 'c', ''], [-1, -1, 0, -1, 2], [-1, -1, 1, -1, 3], 4)
Expected Output: '(a+b)*c'
Explanation: The `+` subtree has lower precedence than `*`, so it must stay parenthesized.
Input: (['var', 'var', 'var', '*', '+'], ['a', 'b', 'c', '', ''], [-1, -1, -1, 1, 0], [-1, -1, -1, 2, 3], 4)
Expected Output: 'a+b*c'
Explanation: Multiplication already binds tighter than addition, so no extra parentheses are needed.
Input: (['var', 'var', 'var', '+', '-'], ['a', 'b', 'c', '', ''], [-1, -1, -1, 1, 0], [-1, -1, -1, 2, 3], 4)
Expected Output: 'a-(b+c)'
Explanation: Without parentheses, `a-b+c` would mean `(a-b)+c`, which changes the value.
Input: (['var', 'var', '-', 'var', '-'], ['a', 'b', '', 'c', ''], [-1, -1, 0, -1, 2], [-1, -1, 1, -1, 3], 4)
Expected Output: 'a-b-c'
Explanation: Because operators of the same precedence are read left to right, `(a-b)-c` can be written as `a-b-c`.
Input: (['var', 'var', 'var', '*', '/'], ['a', 'b', 'c', '', ''], [-1, -1, -1, 1, 0], [-1, -1, -1, 2, 3], 4)
Expected Output: 'a/(b*c)'
Explanation: Dropping the parentheses would produce `a/b*c`, which means `(a/b)*c`, not `a/(b*c)`.
Input: (['var', 'var', 'var', '-', '+'], ['a', 'b', 'c', '', ''], [-1, -1, -1, 1, 0], [-1, -1, -1, 2, 3], 4)
Expected Output: 'a+b-c'
Explanation: For the right child of `+`, same-precedence subtraction does not need parentheses: `a+(b-c)` can be written as `a+b-c`.
Input: (['var', 'var', 'var', '-', '-'], ['a', 'b', 'c', '', ''], [-1, -1, -1, 1, 0], [-1, -1, -1, 2, 3], 4)
Expected Output: 'a-(b-c)'
Explanation: Here the right child of `-` must stay wrapped. `a-b-c` would mean `(a-b)-c`, which is different.
Input: (['var', 'var', 'var', '/', '*'], ['x', 'y', 'z', '', ''], [-1, -1, -1, 1, 0], [-1, -1, -1, 2, 3], 4)
Expected Output: 'x*y/z'
Explanation: For ordinary arithmetic, `x*(y/z)` can be written as `x*y/z` without changing meaning.
Input: (['var', 'var', 'var', '/', '/'], ['a', 'b', 'c', '', ''], [-1, -1, -1, 1, 0], [-1, -1, -1, 2, 3], 4)
Expected Output: 'a/(b/c)'
Explanation: The right child of `/` with the same precedence must keep parentheses. `a/b/c` means `(a/b)/c`.
Input: (['var', 'var', '+', 'var', '/'], ['foo', 'bar', '', 'baz', ''], [-1, -1, 0, -1, 2], [-1, -1, 1, -1, 3], 4)
Expected Output: '(foo+bar)/baz'
Explanation: Multi-character identifiers are allowed. The sum must stay parenthesized because it is the left child of division.
Hints
- Compare a child node's operator precedence with its parent's. Lower precedence children usually need parentheses; higher precedence children do not.
- The tricky case is when parent and child have the same precedence. The left child is safe without parentheses, but the right child depends on the parent operator. Compare `a+(b-c)` with `a-(b-c)`, and `x*(y/z)` with `x/(y/z)`.