You are given the root
of a binary tree with n
nodes where each node
in the tree has node.val
coins. There are n
coins in total throughout the whole tree.
In one move, we may choose two adjacent nodes and move one coin from one node to another. A move may be from parent to child, or from child to parent.
Return the minimum number of moves required to make every node have exactly one coin.
Example 1:
Consider the following binary tree:
3
/ \
0 0
What is the minimum number of moves required to distribute the coins such that each node has exactly one coin?
Example 2:
Consider the following binary tree:
0
/ \
3 0
What is the minimum number of moves required to distribute the coins such that each node has exactly one coin?
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:
Imagine each part of the tree (each node) is like a person who either needs coins or has too many. The brute force approach is to try every possible way of moving coins around until everyone has exactly one coin.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, value=0, left=None, right=None):
self.value = value
self.left = left
self.right = right
def distribute_coins_brute_force(root):
min_moves = float('inf')
def calculate_moves(node, moves):
nonlocal min_moves
if not node:
return 0
# Recursively process left and right subtrees.
left_coins = calculate_moves(node.left, moves)
right_coins = calculate_moves(node.right, moves)
coins_needed = 1 - node.value
total_coins_to_redistribute = left_coins + right_coins + node.value
# If the current node is None, it contributes zero coins and zero moves
if node is None:
return 0
# Explore all possible coin distributions.
if node.left:
moves += abs(left_coins)
if node.right:
moves += abs(right_coins)
if not node.left and not node.right:
return node.value - 1
coins_at_node = node.value + left_coins + right_coins
coins_to_pass_up = coins_at_node - 1
# If all nodes have been visited, update the minimum moves.
if not root.left and not root.right:
min_moves = min(min_moves, moves)
return coins_to_pass_up
total_moves = 0
calculate_moves(root, total_moves)
return int(min_moves)
The core idea is to think about how many coins each part of the tree needs to either give or receive. We move coins around the tree, counting the number of moves needed to balance everything out. We handle this efficiently by solving smaller sub-problems (the subtrees) and combining their results to solve the bigger problem.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def distribute_coins(root):
number_of_moves = 0
def dfs(node):
nonlocal number_of_moves
if not node:
return 0
# Recursively calculate the balance for the left and right subtrees.
left_subtree_balance = dfs(node.left)
right_subtree_balance = dfs(node.right)
#The number of moves is incremented by the absolute value of the balance
number_of_moves += abs(left_subtree_balance) + abs(right_subtree_balance)
# Return the balance of the current node to its parent.
# Balance is the excess coins at node and subtrees - 1 coin for node.
return node.val + left_subtree_balance + right_subtree_balance - 1
dfs(root)
return number_of_moves
Case | How to Handle |
---|---|
Null or empty tree | Return 0 if the root is null as no moves are needed. |
Single node tree | Return 0 because a single node tree will always be balanced. |
Tree with all nodes having 1 coin | The algorithm correctly calculates 0 moves since the tree is already balanced. |
Tree with coins concentrated on one side (highly skewed) | The recursive nature of the solution correctly propagates deficits/surpluses and calculates moves for each subtree. |
Tree with maximum allowable nodes (large N) | Ensure the recursion depth doesn't exceed allowable limits, and that integer overflow doesn't occur in the coin counts. |
Node values near Integer.MAX_VALUE or Integer.MIN_VALUE | Use long to avoid integer overflow when calculating the balance at each node and when adding the moves. |
Unbalanced tree structure, deep on one side | Recursive algorithm handles unbalanced trees as it visits each node, calculating the coins moved in each subtree. |
Tree where total coins does not equal the total nodes | The algorithm should still correctly compute the minimum moves required to distribute the existing coins; it does not need to handle cases where the coins don't match the nodes. |