You are given two expression trees, root1
and root2
. Each node in these trees can be one of the following:
Task:
Determine if the two expression trees are equivalent. Two expression trees are considered equivalent if they represent the same algebraic expression, regardless of their structure. Note that the subtraction operator is not commutative (a - b is not the same as b - a).
Clarifications:
Example 1:
Tree 1:
+
/ \
a b
Tree 2:
+
/ \
b a
Output: true
(because a + b is equivalent to b + a)
Example 2:
Tree 1:
-
/ \
a b
Tree 2:
-
/ \
b a
Output: false
(because a - b is not equivalent to b - a)
Example 3:
Tree 1:
+
/ \
a +
/ \
b a
Tree 2:
+
/ \
+
/ \
a a
\
b
Output: true
(because a + (b + a) is equivalent to (a + a) + b, which simplifies to 2a + b)
Constraints:
Let's explore how to determine if two expression trees are equivalent. We'll start with a straightforward, albeit potentially inefficient, approach and then move towards a more optimized solution.
A naive approach would be to traverse both trees and generate all possible infix expressions for each. Then, we would compare the sets of generated expressions. If the sets are equal, the trees are equivalent.
This approach is conceptually simple, but highly inefficient due to the combinatorial explosion of possible expressions, especially considering operator precedence and parentheses.
A more efficient approach involves creating a canonical representation of the expressions represented by the trees. Since we're checking for equivalence, rather than identical structure, we can focus on the variables and their frequencies. We can achieve this by traversing each tree and building a frequency table of variables.
Algorithm:
-
) encountered on the path from the root to the leaf node.Example:
Consider two expression trees representing the expressions a - b + a
and 2a - b
.
a - b + a
will result in a frequency table: {a: 2, b: -1}
2a - b
will result in a frequency table: {a: 2, b: -1}
Since the frequency tables are equal, the expressions are equivalent.
Edge Cases:
Code Example (Python):
class Node:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
def build_frequency_map(root, sign=1, freq_map=None):
if freq_map is None:
freq_map = {}
if root is None:
return freq_map
if root.val == '+':
build_frequency_map(root.left, sign, freq_map)
build_frequency_map(root.right, sign, freq_map)
elif root.val == '-':
build_frequency_map(root.left, sign, freq_map)
build_frequency_map(root.right, -sign, freq_map)
else:
freq_map[root.val] = freq_map.get(root.val, 0) + sign
return freq_map
def are_equivalent(root1, root2):
freq_map1 = build_frequency_map(root1)
freq_map2 = build_frequency_map(root2)
return freq_map1 == freq_map2
# Example Usage:
# Tree 1: a - b + a
root1 = Node('+')
root1.left = Node('-')
root1.right = Node('a')
root1.left.left = Node('a')
root1.left.right = Node('b')
# Tree 2: 2a - b
root2 = Node('-')
root2.left = Node('+')
root2.right = Node('b')
root2.left.left = Node('a')
root2.left.right = Node('a')
print(are_equivalent(root1, root2)) # Output: True
#Tree 3: a + b
root3 = Node('+')
root3.left = Node('a')
root3.right = Node('b')
print(are_equivalent(root1, root3)) # Output: False