1038. Binary Search Tree to Greater Sum Tree
Problem Description
You are given the root of a Binary Search Tree (BST). Your task is to convert it into a Greater Tree where each node's value is replaced by the sum of its original value plus all values in the BST that are greater than it.
In a BST, remember that:
- All nodes in the left subtree have values less than the current node
- All nodes in the right subtree have values greater than the current node
- Both subtrees are also valid BSTs
For example, if you have a BST with values [1, 2, 3, 4, 5]
, after conversion:
- Node with value
5
becomes5
(no values greater than it) - Node with value
4
becomes4 + 5 = 9
- Node with value
3
becomes3 + 5 + 4 = 12
- Node with value
2
becomes2 + 5 + 4 + 3 = 14
- Node with value
1
becomes1 + 5 + 4 + 3 + 2 = 15
The solution uses a reverse in-order traversal (right → root → left) to visit nodes from largest to smallest value. As we traverse, we maintain a running sum s
that accumulates all values seen so far. When we visit each node, we add its current value to the sum, then update the node's value to this new sum. This ensures each node gets the sum of itself and all greater values.
The traversal order is crucial because in a BST, visiting nodes in right-root-left order means we encounter values in descending order, allowing us to accumulate the sum of greater values as we go.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: A tree is a special type of graph (specifically, a connected acyclic graph). Binary Search Trees are graph structures with nodes and edges connecting parent nodes to their children.
Is it a tree?
- Yes: The problem explicitly states we're working with a Binary Search Tree (BST), which is a tree data structure where each node has at most two children.
DFS
- We arrive at DFS: Since we're dealing with a tree structure, the flowchart leads us directly to using Depth-First Search (DFS).
Conclusion: The flowchart suggests using a DFS approach for this problem. This makes sense because:
- We need to traverse all nodes in the tree to calculate the sum of greater values
- The solution requires visiting nodes in a specific order (reverse in-order: right → root → left) to accumulate values from largest to smallest
- DFS allows us to recursively traverse the tree structure and maintain the running sum as we backtrack
- The recursive nature of DFS perfectly matches the recursive structure of the tree, making it ideal for this transformation problem
The DFS pattern is implemented in the solution through the recursive dfs
function that traverses right subtree first, processes the current node, then traverses the left subtree, maintaining a running sum throughout the traversal.
Intuition
The key insight comes from understanding what "sum of all keys greater than the original key" means in a BST. In a BST, all values to the right of a node are greater than it, and all values to the left are smaller. So for any node, we need to add all values that appear to its right in an in-order traversal.
Think about an in-order traversal of a BST - it gives us values in ascending order. If we reverse this and do a reverse in-order traversal (right → root → left), we get values in descending order. This is the crucial observation!
As we traverse in descending order, we can maintain a running sum. When we visit each node:
- We've already visited all nodes with greater values (they came before in our reverse traversal)
- We can add the current node's value to our running sum
- We update the node's value to this accumulated sum
For example, if our BST has values [1, 2, 3, 4, 5]
:
- We first visit
5
(largest), sum =0 + 5 = 5
, node becomes5
- Then visit
4
, sum =5 + 4 = 9
, node becomes9
- Then visit
3
, sum =9 + 3 = 12
, node becomes12
- And so on...
This way, each node automatically gets the sum of itself plus all greater values, because we've been accumulating exactly those values as we traverse from largest to smallest. The beauty of this approach is that we only need one pass through the tree with O(1) extra space for the running sum variable s
.
Learn more about Tree, Depth-First Search, Binary Search Tree and Binary Tree patterns.
Solution Approach
The solution implements a recursive DFS with reverse in-order traversal (right-root-left). Here's how the implementation works:
Core Algorithm Structure:
- We define a helper function
dfs
that recursively traverses the tree - We maintain a global variable
s
to track the running sum of all visited nodes - We traverse in the order: right subtree → current node → left subtree
Step-by-step Implementation:
def dfs(root: Optional[TreeNode]):
if root is None:
return
dfs(root.right) # Visit right subtree first (larger values)
nonlocal s # Access the outer scope variable
s += root.val # Add current value to running sum
root.val = s # Update node with accumulated sum
dfs(root.left) # Visit left subtree last (smaller values)
Key Implementation Details:
-
Base Case: When we encounter a
None
node, we simply return without doing anything -
Recursive Order: The critical part is visiting
root.right
before processing the current node. This ensures we process nodes from largest to smallest value -
Running Sum Update:
- We use
nonlocal s
to modify the outer scope's sum variable - First add the current node's value to
s
:s += root.val
- Then update the node's value to the accumulated sum:
root.val = s
- We use
-
In-place Modification: The tree is modified in-place - we directly update each node's value rather than creating a new tree
The main function simply initializes s = 0
, calls the DFS helper function with the root, and returns the modified tree. The time complexity is O(n) where n is the number of nodes (we visit each node once), and space complexity is O(h) where h is the height of the tree (for the recursion stack).
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small BST example to illustrate the solution approach:
Initial BST: 4 / \ 1 6 / \ / \ 0 2 5 7 \ \ 3 8
We'll perform a reverse in-order traversal (right → root → left) with a running sum s = 0
.
Traversal Steps:
-
Start at root (4), go right to 6, then right to 7, then right to 8
- Visit node 8:
s = 0 + 8 = 8
, update node 8 → 8 - No more right children, process 7
- Visit node 8:
-
Visit node 7:
s = 8 + 7 = 15
, update node 7 → 15- Go left (none exists), backtrack to 6
-
Visit node 6:
s = 15 + 6 = 21
, update node 6 → 21- Go left to 5
-
Visit node 5:
s = 21 + 5 = 26
, update node 5 → 26- No children, backtrack to 6, then to 4
-
Visit node 4:
s = 26 + 4 = 30
, update node 4 → 30- Go left to 1, then right to 2, then right to 3
-
Visit node 3:
s = 30 + 3 = 33
, update node 3 → 33- No children, backtrack to 2
-
Visit node 2:
s = 33 + 2 = 35
, update node 2 → 35- No left child, backtrack to 1
-
Visit node 1:
s = 35 + 1 = 36
, update node 1 → 36- Go left to 0
-
Visit node 0:
s = 36 + 0 = 36
, update node 0 → 36- No children, traversal complete
Final Greater Tree:
30 / \ 36 21 / \ / \ 36 35 26 15 \ \ 33 8
Each node now contains the sum of its original value plus all values greater than it in the original BST. For instance, the original node 4 had values {5, 6, 7, 8} greater than it, so its new value is 4 + 5 + 6 + 7 + 8 = 30.
Solution Implementation
1# Definition for a binary tree node.
2# class TreeNode:
3# def __init__(self, val=0, left=None, right=None):
4# self.val = val
5# self.left = left
6# self.right = right
7
8class Solution:
9 def bstToGst(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
10 """
11 Convert a Binary Search Tree to a Greater Sum Tree where each node's value
12 is replaced by the sum of all values greater than or equal to it.
13
14 Args:
15 root: Root node of the binary search tree
16
17 Returns:
18 Root of the modified tree (same tree structure, modified values)
19 """
20
21 def reverse_inorder_traversal(node: Optional[TreeNode]) -> None:
22 """
23 Perform reverse in-order traversal (right -> root -> left) to visit
24 nodes in descending order and accumulate the sum.
25
26 Args:
27 node: Current node being processed
28 """
29 # Base case: if node is None, return
30 if node is None:
31 return
32
33 # First, traverse the right subtree (larger values in BST)
34 reverse_inorder_traversal(node.right)
35
36 # Process current node: add its value to running sum
37 nonlocal running_sum
38 running_sum += node.val
39
40 # Update current node's value to the accumulated sum
41 node.val = running_sum
42
43 # Finally, traverse the left subtree (smaller values in BST)
44 reverse_inorder_traversal(node.left)
45
46 # Initialize running sum to track cumulative sum of visited nodes
47 running_sum = 0
48
49 # Start the reverse in-order traversal from root
50 reverse_inorder_traversal(root)
51
52 # Return the modified tree
53 return root
54
1/**
2 * Definition for a binary tree node.
3 * public class TreeNode {
4 * int val;
5 * TreeNode left;
6 * TreeNode right;
7 * TreeNode() {}
8 * TreeNode(int val) { this.val = val; }
9 * TreeNode(int val, TreeNode left, TreeNode right) {
10 * this.val = val;
11 * this.left = left;
12 * this.right = right;
13 * }
14 * }
15 */
16class Solution {
17 // Running sum to accumulate values from greater nodes
18 private int runningSum;
19
20 /**
21 * Converts a Binary Search Tree to a Greater Sum Tree.
22 * Each node's value is replaced with the sum of all nodes greater than or equal to it.
23 *
24 * @param root The root of the binary search tree
25 * @return The root of the modified tree (same reference)
26 */
27 public TreeNode bstToGst(TreeNode root) {
28 runningSum = 0;
29 reverseInorderTraversal(root);
30 return root;
31 }
32
33 /**
34 * Performs a reverse in-order traversal (right -> node -> left).
35 * This visits nodes in descending order for a BST.
36 * Updates each node's value to include the sum of all greater nodes.
37 *
38 * @param node The current node being processed
39 */
40 private void reverseInorderTraversal(TreeNode node) {
41 // Base case: if node is null, return
42 if (node == null) {
43 return;
44 }
45
46 // Process right subtree first (greater values in BST)
47 reverseInorderTraversal(node.right);
48
49 // Update running sum with current node's value
50 runningSum += node.val;
51
52 // Replace current node's value with the running sum
53 node.val = runningSum;
54
55 // Process left subtree (smaller values in BST)
56 reverseInorderTraversal(node.left);
57 }
58}
59
1/**
2 * Definition for a binary tree node.
3 * struct TreeNode {
4 * int val;
5 * TreeNode *left;
6 * TreeNode *right;
7 * TreeNode() : val(0), left(nullptr), right(nullptr) {}
8 * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
9 * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
10 * };
11 */
12class Solution {
13public:
14 /**
15 * Converts a Binary Search Tree to a Greater Sum Tree
16 * where each node's value is updated to the sum of all values
17 * greater than or equal to the node's original value
18 *
19 * @param root The root of the BST
20 * @return The root of the modified tree (same tree, values updated)
21 */
22 TreeNode* bstToGst(TreeNode* root) {
23 int runningSum = 0; // Accumulator for the running sum of node values
24
25 // Define recursive lambda function for reverse in-order traversal
26 auto reverseInorderDFS = [&](this auto&& reverseInorderDFS, TreeNode* currentNode) -> void {
27 // Base case: if node is null, return
28 if (!currentNode) {
29 return;
30 }
31
32 // Traverse right subtree first (larger values in BST)
33 reverseInorderDFS(currentNode->right);
34
35 // Process current node: add its value to running sum and update the node
36 runningSum += currentNode->val;
37 currentNode->val = runningSum;
38
39 // Traverse left subtree (smaller values in BST)
40 reverseInorderDFS(currentNode->left);
41 };
42
43 // Start the traversal from root
44 reverseInorderDFS(root);
45
46 return root;
47 }
48};
49
1/**
2 * Definition for a binary tree node.
3 * class TreeNode {
4 * val: number
5 * left: TreeNode | null
6 * right: TreeNode | null
7 * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
8 * this.val = (val===undefined ? 0 : val)
9 * this.left = (left===undefined ? null : left)
10 * this.right = (right===undefined ? null : right)
11 * }
12 * }
13 */
14
15/**
16 * Converts a Binary Search Tree to a Greater Sum Tree
17 * where each node's value is replaced by the sum of all values greater than or equal to it
18 * @param root - The root node of the binary search tree
19 * @returns The root of the modified tree (Greater Sum Tree)
20 */
21function bstToGst(root: TreeNode | null): TreeNode | null {
22 // Running sum to accumulate values during traversal
23 let runningSum: number = 0;
24
25 /**
26 * Performs reverse in-order traversal (right -> node -> left)
27 * to process nodes from largest to smallest value
28 * @param node - Current node being processed
29 */
30 const reverseInorderTraversal = (node: TreeNode | null): void => {
31 // Base case: if node is null, return
32 if (!node) {
33 return;
34 }
35
36 // First, traverse the right subtree (larger values)
37 reverseInorderTraversal(node.right);
38
39 // Process current node: add its value to running sum
40 runningSum += node.val;
41
42 // Update current node's value to the running sum
43 node.val = runningSum;
44
45 // Finally, traverse the left subtree (smaller values)
46 reverseInorderTraversal(node.left);
47 };
48
49 // Start the traversal from root
50 reverseInorderTraversal(root);
51
52 // Return the modified tree
53 return root;
54}
55
Time and Space Complexity
The time complexity is O(n)
, where n
is the number of nodes in the binary search tree. This is because the algorithm performs a depth-first search (DFS) traversal that visits each node exactly once. The DFS function is called once for each node in the tree, and at each node, it performs constant time operations (adding to the sum s
and updating the node's value).
The space complexity is O(n)
, where n
is the number of nodes in the binary search tree. This space is used by the recursive call stack during the DFS traversal. In the worst case, when the tree is completely unbalanced (essentially a linked list), the recursion depth can reach n
levels deep, requiring O(n)
space on the call stack. In the best case of a balanced tree, the space complexity would be O(log n)
for the recursion stack, but we consider the worst-case scenario for complexity analysis.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting to Use nonlocal
for the Running Sum
One of the most common mistakes is forgetting to declare nonlocal running_sum
inside the nested function. Without this declaration, Python treats running_sum
as a local variable when you try to modify it, leading to an UnboundLocalError
.
Incorrect Code:
def bstToGst(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
def reverse_inorder_traversal(node):
if node is None:
return
reverse_inorder_traversal(node.right)
running_sum += node.val # ERROR: UnboundLocalError
node.val = running_sum
reverse_inorder_traversal(node.left)
running_sum = 0
reverse_inorder_traversal(root)
return root
Solution:
Always include nonlocal running_sum
before modifying the variable, or use a mutable container like a list [0]
to store the sum:
# Option 1: Using nonlocal nonlocal running_sum running_sum += node.val # Option 2: Using a list to avoid nonlocal running_sum = [0] # In main function running_sum[0] += node.val # In helper function node.val = running_sum[0]
2. Incorrect Traversal Order
Another critical mistake is using standard in-order traversal (left → root → right) instead of reverse in-order traversal. This would process nodes from smallest to largest, giving incorrect results.
Incorrect Code:
def reverse_inorder_traversal(node):
if node is None:
return
reverse_inorder_traversal(node.left) # WRONG: Processing left first
nonlocal running_sum
running_sum += node.val
node.val = running_sum
reverse_inorder_traversal(node.right) # WRONG: Processing right last
This would accumulate sums in ascending order, causing each node to contain the sum of all values less than or equal to it, which is the opposite of what we want.
Solution: Always maintain the correct order: right → root → left to ensure you're processing nodes from largest to smallest value.
3. Creating a New Tree Instead of Modifying In-Place
Some implementations might accidentally create new nodes instead of modifying the existing tree, which wastes memory and may not meet the problem requirements.
Incorrect Approach:
def bstToGst(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
def build_greater_tree(node):
if node is None:
return None
new_node = TreeNode() # Creating new nodes unnecessarily
new_node.right = build_greater_tree(node.right)
nonlocal running_sum
running_sum += node.val
new_node.val = running_sum
new_node.left = build_greater_tree(node.left)
return new_node
Solution:
Modify the existing nodes directly by updating node.val
rather than creating new TreeNode objects. This is more efficient and typically what the problem expects.
Consider the classic dynamic programming of fibonacci numbers, what is the recurrence relation?
Recommended Readings
Everything About Trees A tree is a type of graph data structure composed of nodes and edges Its main properties are It is acyclic doesn't contain any cycles There exists a path from the root to any node Has N 1 edges where N is the number of nodes in the tree and
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
Binary Search Tree Intro Definition A Binary Search Tree or BST is a rooted binary tree with the value of each internal node being greater than all the values in the respective node's left subtree and less than the ones in its right subtree An empty tree is a BST since it satisfies the
Want a Structured Path to Master System Design Too? Don’t Miss This!