Taro Logo

Maximum Average Subtree

Medium
Amazon logo
Amazon
1 view
Topics:
TreesRecursion

Given the root of a binary tree, each node has an integer value. Find the subtree with the largest average value.

For example, consider the following binary tree:

      1
     / \
    2   3
   / \
  4   5

The subtree rooted at node 2 has nodes with values 2, 4, and 5. The sum of these values is 11, and the number of nodes is 3. Therefore, the average value of this subtree is 11/3 ≈ 3.67.

The subtree rooted at node 1 has nodes with values 1, 2, 3, 4, and 5. The sum of these values is 15, and the number of nodes is 5. Therefore, the average value of this subtree is 15/5 = 3.

In this example, the subtree rooted at node 2 has the largest average value (3.67).

Write a function that takes the root of a binary tree as input and returns the largest average value among all subtrees.

Here is another example:

      5
     / \
    6   1

In this case, the possible subtrees and their averages are:

  • Node 5: Average = 5 / 1 = 5
  • Node 6: Average = 6 / 1 = 6
  • Node 1: Average = 1 / 1 = 1
  • Subtree rooted at 5: Average = (5 + 6 + 1) / 3 = 4

The maximum average subtree is the subtree rooted at node 6, with an average of 6.

Solution


Maximum Average Subtree

Problem Description

Given the root of a binary tree, find the maximum average value of any subtree of that tree.

Each node in the tree has a value. The average value of a subtree is defined as the sum of the values of all nodes in the subtree divided by the number of nodes in the subtree.

Naive Solution

A brute-force solution would involve traversing every node in the tree and considering it as the root of a subtree. For each subtree, calculate the sum of its nodes and the number of nodes. Then calculate the average. Keep track of the maximum average seen so far.

This approach is inefficient because calculating the sum and count for each subtree involves redundant calculations. For example, when considering a node's grandparent as the root, the sums and counts for the node and its sibling's subtrees are recalculated even though they were already computed when the node and sibling were considered as roots.

Optimal Solution

A more efficient approach involves a post-order traversal of the tree. During the traversal, for each node, we calculate:

  1. The sum of the values of all nodes in its subtree (including the node itself).
  2. The number of nodes in its subtree (including the node itself).

We can then calculate the average value of the subtree rooted at that node. We keep track of the maximum average encountered during the traversal.

This approach avoids redundant calculations by calculating the sum and count for each subtree only once. The post-order traversal ensures that the sums and counts for a node's children are calculated before calculating the sum and count for the node itself.

Edge Cases

  • Empty Tree: If the root is null, return 0.
  • Single Node Tree: If the root has no children, the maximum average is simply the root's value.
  • Negative Values: The tree can contain nodes with negative values, which should be correctly factored into the average calculation.

Code (Python)

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def maximumAverageSubtree(self, root: TreeNode) -> float:
        max_avg = float('-inf')

        def postorder(node):
            nonlocal max_avg
            if not node:
                return 0, 0  # (sum, count)

            left_sum, left_count = postorder(node.left)
            right_sum, right_count = postorder(node.right)

            subtree_sum = node.val + left_sum + right_sum
            subtree_count = 1 + left_count + right_count

            avg = subtree_sum / subtree_count
            max_avg = max(max_avg, avg)

            return subtree_sum, subtree_count

        postorder(root)
        return max_avg

Time and Space Complexity

  • Time Complexity: O(N), where N is the number of nodes in the tree. We visit each node exactly once during the post-order traversal.
  • Space Complexity: O(H), where H is the height of the tree. This is due to the recursive call stack during the post-order traversal. In the worst case (skewed tree), H can be equal to N, resulting in O(N) space complexity. In the best case (balanced tree), H would be log(N), resulting in O(log N) space complexity.