Start learning free

LESSON 305 OF 347 · BINARY TREES

Max Sum BST in C#: Check Every Subtree Directly

Practice O(n2) Read the full explanation

Given a binary tree root, the task is to return the maximum sum of all keys of any sub-tree which is also a Binary Search Tree (BST).

Assume a BST is defined as follows:

  • The left subtree of a node contains only nodes with keys less than the node's key.
  • The right subtree of a node contains only nodes with keys greater than the node's key.
  • Both the left and right subtrees must also be binary search trees.

 

Example 1:

Input: root = [1,4,3,2,4,2,5,null,null,null,null,null,null,4,6]
Output: 20
Explanation: Maximum sum in a valid Binary search tree is obtained in root node with key equal to 3.

Example 2:

Input: root = [4,3,null,1,2]
Output: 2
Explanation: Maximum sum in a valid Binary search tree is obtained in a single root node with key equal to 2.

Example 3:

Input: root = [-4,-2,-5]
Output: 0
Explanation: All values are negatives. Return an empty BST.

Example 4:

Input: root = [2,1,3]
Output: 6

Example 5:

Input: root = [5,4,8,3,null,6,3]
Output: 7

 

Constraints:

  • Each tree has at most 40000 nodes..
  • Each node's value is between [-4 * 10^4 , 4 * 10^4].
STEP 1 OF 19
    Ctrl + Enter to check

    C# · BINARY TREES

    Max Sum Bst in C#, explained

    Problem overview

    The method MaxSumBST receives the root of a binary tree and returns the largest sum of keys over all subtrees that are also binary search trees. In a BST, every key in a node's left subtree is strictly less than the node's key, every key in its right subtree is strictly greater, and both subtrees are BSTs themselves. A subtree here means a node together with everything below it.

    Some small examples:

    • [2, 1, 3] is itself a BST, so the answer is 2 + 1 + 3 = 6.
    • [4, 3, null, 1, 2]: the node 3 has the child 2 on its right, which is less than 3, so the subtree at 3 is not a BST. The root isn't one either. The leaves are BSTs, and the best is the leaf 2, so the answer is 2.
    • [-4, -2, -5]: every sum is negative, and the empty tree counts as a BST with sum 0, so the answer is 0.
    • [5, 4, 8, 3, null, 6, 3]: the node 8 has a right child 3, which is not greater than 8, and the root is therefore not a BST either. The subtree at 4 with its child 3 is a BST with sum 7, which is the best.

    Approach: ask the question at every node

    This lesson's approach is the most direct one, and it splits the problem into small helpers that each do one job:

    1. Visit every node of the tree. Each node is the root of one subtree.
    2. For that subtree, decide whether it is a BST.
    3. If it is, compute its sum and keep the largest one seen.

    To decide whether a subtree is a BST, the lesson checks the definition literally: both children's subtrees are BSTs, everything on the left is less than the node, and everything on the right is greater. The "everything" checks must look at the whole subtree, not only the direct children. Checking only the children misses a tree like [5, 3, 8, null, 7], where the node 7 is greater than its parent 3 but sits to the left of 5.

    This is easy to write and read, but it repeats work: a node deep in the tree is examined again for every ancestor. In the worst case, a tree shaped like a chain, that adds up to O(n²) time. The second lesson on this problem removes the repetition by returning facts from the recursion.

    Step by step

    1. Keep the best answer in a field. Declare int result in the class and set it to 0 in a public constructor Solution(). A field is visible to every method, so the search can update it. Starting at 0 also covers the empty BST.
    2. Call the search. MaxSumBST calls Dfs(root) and returns result.
    3. Declare Dfs as a public void method taking a TreeNode root.
    4. Stop at null. An empty subtree needs no work, so use a bare return.
    5. Recurse first. Call Dfs on root.left and on root.right so every subtree below this node is checked.
    6. Test the current subtree with IsBST(root), a helper written later.
    7. Update the answer with Math.Max(result, Sum(root)) when the subtree is a BST.
    8. Write Sum. For null it returns 0. Otherwise it returns root.val plus the sums of both children.
    9. Write IsBST. An empty tree is a BST, so null returns true.
    10. Combine four checks with &&: the left subtree is a BST, the right subtree is a BST, everything on the left is less than root.val, and everything on the right is greater.
    11. Write EverythingLess(root, val). For null it returns true. Otherwise root.val < val must hold, and the same must hold for both children with the same val.
    12. Write EverythingGreater(root, val), its mirror image with the comparison reversed.

    The solution in C#

    class Solution {
    
        int result;
        public Solution() {
            result = 0;
        }
    
        public int Sum(TreeNode root) {
            if (root == null) {
                return 0;
            }
            return root.val + Sum(root.left) + Sum(root.right);
        }
    
        public bool EverythingLess(TreeNode root, int val) {
            if (root == null) {
                return true;
            }
            return root.val < val &&
            EverythingLess(root.left, val) &&
            EverythingLess(root.right, val);
        }
    
        public bool EverythingGreater(TreeNode root, int val) {
            if (root == null) {
                return true;
            }
            return root.val > val &&
            EverythingGreater(root.left, val) &&
            EverythingGreater(root.right, val);
        }
    
        public bool IsBST(TreeNode root) {
            if (root == null) {
                return true;
            }
            return IsBST(root.left) &&
            IsBST(root.right) &&
            EverythingLess(root.left, root.val) &&
            EverythingGreater(root.right, root.val);
        }
    
        public void Dfs(TreeNode root) {
            if (root == null) {
                return;
            }
            Dfs(root.left);
            Dfs(root.right);
            if (IsBST(root)) {
                result = Math.Max(result, Sum(root));
            }
        }
    
        public int MaxSumBST(TreeNode root) {
            Dfs(root);
            return result;
        }
    };
    

    The C# points to notice:

    • A field and a constructor. int result; is an instance field. It starts at 0 anyway, because C# gives fields their default values, and the constructor sets it explicitly, which makes the intent visible. A new Solution object starts with a clean answer.
    • Short-circuit &&. The checks in IsBST and in the two helpers stop at the first false, so a failing subtree stops being examined early. A long expression can be split across lines, as the solution does.
    • Strict comparisons. The BST rule uses < and >, not <= and >=, so a subtree with two equal keys in the wrong places is not a BST.
    • Name MaxSumBST. The uppercase BST in the method name is part of the lesson's API, so keep it exactly.

    Complexity

    Time: O(n²) in the worst case. Dfs visits each of the n nodes. At each one, IsBST and Sum may walk the whole subtree below it. For a chain of n nodes, the subtree sizes are n, n - 1, ..., 1, which adds up to about n²/2 steps. For a balanced tree the total is closer to O(n log n).

    Space: O(h) for the recursion, where h is the height. A chain of 40,000 nodes makes the recursion that deep.

    Edge cases and common mistakes

    • All keys negative. Starting result at 0 returns 0, the empty BST, as the third example expects. Starting at int.MinValue would return a negative number.
    • Checking only the direct children. A key deep in the left subtree can still be larger than the node. That is why EverythingLess and EverythingGreater walk the whole subtree.
    • Equal keys. A node with a child holding the same key is not a BST under this definition.
    • A BST inside a non-BST. The root may fail while a deep subtree succeeds, so Dfs keeps going after a failed check and tests every node.
    • Forgetting the base case in a helper. Each of the four recursive helpers needs a null check, with the right answer: 0 for Sum and true for the three checks.

    Testing

    • [2, 1, 3] gives 6.
    • [4, 3, null, 1, 2] gives 2.
    • [-4, -2, -5] gives 0.
    • [5, 4, 8, 3, null, 6, 3] gives 7.
    • [1, 4, 3, 2, 4, 2, 5, null, null, null, null, null, null, 4, 6] gives 20, from the subtree rooted at 3.
    Keep going with C#About 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free