Find the Subtree with the Maximum Average in a Binary Tree using JavaScript

Last Updated : 17 May, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

Given a binary tree, our task is to find the subtree with the maximum average in JavaScript. The average of a subtree is defined as the sum of all node values within the subtree divided by the number of nodes it contains.

Example:

Input:
5
/ \
6 7
/ \
3 4
Output: 7
Explanation
Average of values of subtree of node 5 = (5 + 6 + 7+3 +4) / 5 = 5
Average of values of subtree of node 6 = (6+3 +4) / 3 = 4.33
Average of values of subtree of node 7 = 7 / 1 = 7
Average of values of subtree of node 3 = 3 / 1 = 3
Average of values of subtree of node 4 = 4 / 1 = 4

Below are the approaches to finding the subtree with the maximum average in a binary tree using JavaScript:

Brute Force Approach (DFS for each node)

Initialize maxAverage and maxAvgSubtreeR to track the highest average and its corresponding subtree root. Conduct a DFS traversal of the binary tree. For each node, compute the sum of values and the node count using recursive functions subtSum and nodeCount. Determine the subtree's average and update maxAverage and maxAvgSubtreeR if the average is higher. Finally, return the subtree root with the highest average.
Example: The example below shows how to find the subtree with the maximum average in a binary tree using JavaScript.

JavaScript
class TreeNode {
    constructor(val) {
        this.val = val;
        this.left = this.right = null;
    }
}

const subtSum = (node) => {
    if (!node) return 0;
    return node.val + subtSum(node.left) 
                    + subtSum(node.right);
}

const nodeCount = (node) => {
    if (!node) return 0;
    return 1 + nodeCount(node.left) 
             + nodeCount(node.right);
}

const findMAvgSub = (root) => {
    let maxAverage = -Infinity;
    let maxAvgStreeR = null;

    const dfs = (node) => {
        if (!node) return;

        const sum = subtSum(node);
        const count = nodeCount(node);
        const average = sum / count;

        if (average > maxAverage) {
            maxAverage = average;
            maxAvgStreeR = node;
        }

        dfs(node.left);
        dfs(node.right);
    };

    dfs(root);
    return maxAvgStreeR;
};

// Constructing the binary tree
const root1 = new TreeNode(5);
root1.left = new TreeNode(6);
root1.right = new TreeNode(7);
root1.left.left = new TreeNode(3);
root1.left.right = new TreeNode(4);

// Finding the maximum average subtree
const result1 = findMAvgSub(root1);
console.log("Maximum average subtree root value:",
                                    result1.val);

Output
Maximum average subtree root value: 7

Time Complexity: O(n^2), where n is the number of nodes in the binary tree.

Space Complexity: O(h), where h is the height of the binary tree.

Bottom-Up DFS with Recursion

In this method, a bottom-up depth-first search (DFS) is carried out recursively. For every node reached during the traversal, the sum and count of nodes for its left and right subtrees are calculated recursively. Afterward, the sum and count of nodes for the current subtree are determined by adding the sums and counts of its left and right subtrees, alongside the current node. Subsequently, the average of the current subtree is computed, and the maximum average is adjusted accordingly.
Example: The example below shows how to Find the subtree with the maximum average in a binary tree using Bottom-Up DFS with Recursion.

JavaScript
class TreeNode {
    constructor(val) {
        this.val = val;
        this.left = this.right = null;
    }
}

const maxAvgSubt = (root) => {
    const calSubtreeS = (node) => {
        if (!node) return { sum: 0, count: 0 };

        const left = calSubtreeS(node.left);
        const right = calSubtreeS(node.right);

        const sum = left.sum + right.sum + node.val;
        const count = left.count + right.count + 1;

        return { sum, count };
    };

    let maxAverage = Number.MIN_SAFE_INTEGER;

    const dfs = (node) => {
        if (!node) return;

        const { sum, count } = calSubtreeS(node);
        const average = sum / count;
        maxAverage = Math.max(maxAverage, average);

        dfs(node.left);
        dfs(node.right);
    };

    dfs(root);
    return maxAverage;
};

// Constructing the binary tree
const root2 = new TreeNode(5);
root2.left = new TreeNode(6);
root2.right = new TreeNode(7);
root2.left.left = new TreeNode(3);
root2.left.right = new TreeNode(4);

// Finding the maximum average subtree
const result2 = maxAvgSubt(root2);
console.log("Maximum average subtree:", result2);

Output
Maximum average subtree: 7

Time Complexity: O(n), where n is the number of nodes in the binary tree.

Space Complexity: O(h), where h is the height of the binary tree.

Optimized Bottom-Up DFS with Recursion

In this method, for each node during traversal, recursively calculate the sum and count of nodes for its left and right subtrees. Compute the sum and count of nodes for the current subtree by adding the sums and counts of its left and right subtrees, along with the current node. Calculate the average of the current subtree. Update maxAverage if the average of the current subtree is greater than the current maximum average. Return the maximum average found.

Example: The example below shows how to Find the subtree with the maximum average in a binary tree using Optimized Bottom-Up DFS with Recursion.

JavaScript
class TreeNode {
    constructor(val) {
        this.val = val;
        this.left = this.right = null;
    }
}

const maxAvgeSubtree = (root) => {
    const dfs = (node) => {
        if (!node) return { sum: 0, count: 0 };

        const left = dfs(node.left);
        const right = dfs(node.right);

        const sum = left.sum + right.sum + node.val;
        const count = left.count + right.count + 1;
        const average = sum / count;

        maxAverage = Math.max(maxAverage, average);

        return { sum, count };
    };

    let maxAverage = Number.MIN_SAFE_INTEGER;
    dfs(root);
    return maxAverage;
};

// Constructing the binary tree
const root3 = new TreeNode(5);
root3.left = new TreeNode(6);
root3.right = new TreeNode(7);
root3.left.left = new TreeNode(3);
root3.left.right = new TreeNode(4);

// Finding the maximum average subtree
const result3 = maxAvgeSubtree(root3);
console.log("Maximum average subtree:", result3);

Output
Maximum average subtree: 7

Time Complexity: O(n), where n is the number of nodes in the binary tree.

Space Complexity: O(h), where h is the height of the binary tree.

Iterative Bottom-Up DFS with Stack

In this approach, for each node encountered during traversal, Use a secondary stack to perform DFS and calculate the sum and count of nodes for the subtree rooted at the current node. Compute the average of the current subtree. Update maxAverage if the average of the current subtree is greater than the current maximum average. Return the maximum average found.

Example: The example below shows how to find the subtree with the maximum average in a binary tree using Iterative Bottom-Up DFS with Stack.

JavaScript
class TreeNode {
    constructor(val) {
        this.val = val;
        this.left = this.right = null;
    }
}

const maxAvgeSubtree = (root) => {
    let maxAverage = Number.MIN_SAFE_INTEGER;
    const stack = [root];

    while (stack.length) {
        const node = stack.pop();
        let sum = 0, count = 0;

        const stack2 = [node];
        while (stack2.length) {
            const curr = stack2.pop();
            sum += curr.val;
            count++;

            if (curr.left) stack.push(curr.left);
            if (curr.right) stack.push(curr.right);
        }

        const average = sum / count;
        maxAverage = Math.max(maxAverage, average);
    }

    return maxAverage;
};

// Constructing the binary tree
const root4 = new TreeNode(5);
root4.left = new TreeNode(6);
root4.right = new TreeNode(7);
root4.left.left = new TreeNode(3);
root4.left.right = new TreeNode(4);

// Finding the maximum average subtree
const result4 = maxAvgeSubtree(root4);
console.log("Maximum average subtree:", result4);

Output
Maximum average subtree: 7

Time Complexity: O(n), where n is the number of nodes in the binary tree.

Space Complexity: O(h), where h is the height of the binary tree.

Top-Down DFS with Memoization

In this approach, each node is encountered during traversal. Recursively calculate the sum and count of nodes for their left and right subtrees, memoizing the results to avoid redundant computations. Compute the sum and count of nodes for the current subtree by adding the sums and counts of its left and right subtrees, along with the current node. Calculate the average of the current subtree. Update maxAverage if the average of the current subtree is greater than the current maximum average. Return the maximum average found.

Example: The example below shows how to find the subtree with the maximum average in a binary tree using Top-Down DFS with Memoization.

JavaScript
class TreeNode {
    constructor(val) {
        this.val = val;
        this.left = this.right = null;
    }
}

const maxAvgeSubtree = (root) => {
    const memo = new Map();

    const dfs = (node) => {
        if (!node) 
            return { sum: 0, count: 0 };

        if (memo.has(node)) 
            return memo.get(node);

        const left = dfs(node.left);
        const right = dfs(node.right);

        const sum = left.sum + right.sum + node.val;
        const count = left.count + right.count + 1;
        const average = sum / count;

        memo.set(node, { sum, count });

        return { sum, count };
    };

    let maxAverage = Number.MIN_SAFE_INTEGER;

    const calMaxAvg = (node) => {
        const { sum, count } = dfs(node);
        maxAverage = Math.max(maxAverage, sum / count);

        if (node.left) calMaxAvg(node.left);
        if (node.right) calMaxAvg(node.right);
    };

    calMaxAvg(root);

    return maxAverage;
};

// Constructing the binary tree
const root5 = new TreeNode(5);
root5.left = new TreeNode(6);
root5.right = new TreeNode(7);
root5.left.left = new TreeNode(3);
root5.left.right = new TreeNode(4);

// Finding the maximum average subtree
const result5 = maxAvgeSubtree(root5);
console.log("Maximum average subtree:", result5);

Output
Maximum average subtree: 7

Time Complexity: O(n), where n is the number of nodes in the binary tree.

Space Complexity: O(n), where n is the number of nodes in the binary tree.


Next Article

Similar Reads