You are given the root
of a binary tree that consists of nodes with values 0
or 1
. You are also given an integer result
, which is the desired bitwise OR of all values in the binary tree.
You can flip the value of any node in the binary tree. A flip changes 0
to 1
or 1
to 0
.
Return the minimum number of flips required to make the bitwise OR of all node values in the tree equal to result
. If it is impossible to make the bitwise OR equal to result
, return -1
.
Example 1:
Input: root = [1,1,0], result = 1 Output: 0 Explanation: The bitwise OR of all node values is 1. The result we want equals the bitwise OR, so we don't have to flip any nodes.
Example 2:
Input: root = [1,1,0], result = 0 Output: 2 Explanation: We have to flip the values of the root and one of its children to make the bitwise OR equal to 0.
Example 3:
Input: root = [0,0,0], result = 1 Output: 3 Explanation: We have to flip the value of each node to make the bitwise OR equal to 1.
Constraints:
[1, 105]
.0
or 1
.result
is either 0
or 1
.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force strategy involves exploring every possible combination of flips at each node in the binary tree. For each combination, we evaluate the entire expression tree to see if it produces the desired result. We then choose the combination with the fewest number of flips that satisfies the desired output.
Here's how the algorithm would work step-by-step:
class Node:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
def minimum_flips_brute_force(root, desired_result):
minimum_number_of_flips = float('inf')
def evaluate_tree(node):
if node is None:
return None
if node.left is None and node.right is None:
return node.value
left_value = evaluate_tree(node.left)
right_value = evaluate_tree(node.right)
if node.value == 2: # AND operation
return left_value & right_value
else: # OR operation
return left_value | right_value
def explore_flips(node, current_flips):
nonlocal minimum_number_of_flips
if node is None:
result = evaluate_tree(root)
if result == desired_result:
minimum_number_of_flips = min(minimum_number_of_flips, current_flips)
return
# Explore without flipping
original_value = node.value
explore_flips(node.left, current_flips)
# Explore with flipping
node.value = 1 - node.value if node.value in [0, 1] else node.value
# This flips operation nodes
if node.value == 2:
node.value = 3
elif node.value == 3:
node.value = 2
# We increment the flip count
explore_flips(node.left, current_flips + 1)
node.value = original_value
# Restore original value for the parent node
if original_value == 2:
node.value = 2
elif original_value == 3:
node.value = 3
node.value = original_value
# Recursively traverse the right subtree for both flipped and non-flipped scenarios
explore_flips(node.right, current_flips)
# Flip and count increase here
node.value = 1 - node.value if node.value in [0, 1] else node.value
if node.value == 2:
node.value = 3
elif node.value == 3:
node.value = 2
explore_flips(node.right, current_flips + 1)
node.value = original_value
if original_value == 2:
node.value = 2
elif original_value == 3:
node.value = 3
node.value = original_value
explore_flips(root, 0)
if minimum_number_of_flips == float('inf'):
return -1
else:
return minimum_number_of_flips
The best way to solve this is to work from the bottom of the tree upwards. For each part of the tree, we figure out the fewest flips needed to make it result in either a 0 or a 1, based on the desired final result at the very top.
Here's how the algorithm would work step-by-step:
def minimum_flips(root, result): def solve(node): if not node: return (float('inf'), float('inf'))
if node.val == 0 or node.val == 1:
if node.val == 0:
return (0, 1)
else:
return (1, 0)
left_zero_cost, left_one_cost = solve(node.left)
right_zero_cost, right_one_cost = solve(node.right)
if node.val == 2: # AND operation
zero_cost = min(left_zero_cost + right_zero_cost, \
left_zero_cost + right_one_cost, \
left_one_cost + right_zero_cost)
one_cost = left_one_cost + right_one_cost return (zero_cost, one_cost)
elif node.val == 3: # OR operation
zero_cost = left_zero_cost + right_zero_cost
one_cost = min(left_zero_cost + right_one_cost, \
left_one_cost + right_zero_cost, \
left_one_cost + right_one_cost) return (zero_cost, one_cost)
elif node.val == 4: # XOR operation
zero_cost = min(left_zero_cost + right_zero_cost, \
left_one_cost + right_one_cost)
one_cost = min(left_zero_cost + right_one_cost, \
left_one_cost + right_zero_cost)
return (zero_cost, one_cost)
# Calculate min flips for 0 and 1 at root.
zero_flips, one_flips = solve(root)
# Return the minimum flips needed for the final result.
if result == 1:
return one_flips
else:
return zero_flips
Case | How to Handle |
---|---|
Null or Empty Tree | Return 0 if the tree is null or empty, as no flips are needed. |
Single Node Tree | If the single node's value equals the desired result, return 0, otherwise return 1 if flips are allowed, and -1 or error if flips are not allowed in this case. |
Tree with only AND nodes and desired result is 1, but all leaves are 0 | Return -1 or throw an exception indicating that the target result is unreachable given the tree structure and node values. |
Tree with only OR nodes and desired result is 0, but all leaves are 1 | Return -1 or throw an exception indicating that the target result is unreachable given the tree structure and node values. |
Deeply nested tree exceeding recursion depth limits | Consider iterative solutions or memoization to avoid stack overflow errors with deep trees. |
Tree with a very large number of nodes (scalability) | Ensure the chosen approach (e.g., memoization, dynamic programming) has reasonable time and space complexity with respect to the tree size. |
Desired result cannot be achieved with given number of flips available | Return -1 or throw exception when the desired value is not reachable based on the value of nodes and logic gates. |
Integer overflow during intermediate calculations | Consider using larger data types or modular arithmetic if the number of flips or intermediate calculations could potentially overflow. |