538. Convert BST to Greater Tree
Problem Description
You are given the root of a Binary Search Tree (BST). Your task is to transform it into a "Greater Tree" where each node's value is updated to be the sum of its original value plus all values in the tree 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 [4, 1, 6, 0, 2, 5, 7, 3, 8]
, each node should be updated as follows:
- Node with value
8
becomes8
(no values greater than it) - Node with value
7
becomes7 + 8 = 15
- Node with value
6
becomes6 + 7 + 8 = 21
- Node with value
5
becomes5 + 6 + 7 + 8 = 26
- And so on...
The solution uses a reverse in-order traversal (right → root → left) to visit nodes in descending order. By maintaining a running sum s
of all visited nodes, we can efficiently update each node's value to include the sum of all greater values. The traversal order ensures we always process larger values before smaller ones, allowing us to accumulate the sum as we go.
The key insight is that in a BST, a reverse in-order traversal visits nodes from largest to smallest, making it perfect for accumulating the sum of all greater values encountered so far.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: While this is specifically a tree problem, a tree is a special type of graph (connected acyclic graph). The problem involves nodes with connections (parent-child relationships).
Is it a tree?
- Yes: The problem explicitly states we're working with a Binary Search Tree (BST), which is a specialized tree structure where nodes follow a specific ordering property.
DFS
- Yes: Since we're dealing with a tree problem, the flowchart directs us to use Depth-First Search (DFS).
Conclusion: The flowchart suggests using DFS for this tree traversal problem.
This aligns perfectly with our solution approach. The problem requires us to visit every node and update its value based on all nodes with greater values. DFS allows us to traverse the tree systematically. Specifically, we use a modified in-order traversal (right → root → left) to visit nodes in descending order of their values, which is ideal for accumulating the sum of greater values as we traverse.
The DFS approach is optimal here because:
- We need to visit every node exactly once
- The tree structure naturally lends itself to recursive traversal
- By choosing the right traversal order (reverse in-order), we can solve the problem in a single pass
Intuition
The key insight comes from understanding what "greater values" means in a BST. In a BST, all values to the right of a node are greater than that node. If we could somehow visit all the greater values first and keep track of their sum, we could update each node as we encounter it.
Think about the in-order traversal of a BST - it visits nodes in ascending order (left → root → right). What if we reversed this? A reverse in-order traversal (right → root → left) would visit nodes in descending order - from largest to smallest. This is exactly what we need!
By traversing the tree in descending order, we can maintain a running sum of all values we've seen so far. When we reach any node, our running sum already contains all values greater than the current node. We just need to:
- Add the current node's value to our running sum
- Update the current node's value to this new sum
- Continue to smaller values
For example, consider a simple BST: [4, 2, 6, 1, 3, 5, 7]
. A reverse in-order traversal visits in order: 7 → 6 → 5 → 4 → 3 → 2 → 1
.
- When we visit
7
, sum =0
, update node to0 + 7 = 7
- When we visit
6
, sum =7
, update node to7 + 6 = 13
- When we visit
5
, sum =13
, update node to13 + 5 = 18
- And so on...
Each node gets updated with the sum of itself plus all previously visited (greater) values. The beauty of this approach is its simplicity - we solve the problem in a single traversal with just one variable to track the running sum.
Learn more about Tree, Depth-First Search, Binary Search Tree and Binary Tree patterns.
Solution Approach
The implementation uses a recursive DFS with a reverse in-order traversal pattern. Here's how the solution works:
Data Structure and Variables:
- We maintain a single variable
s
as our running sum, initialized to0
- The variable is declared as
nonlocal
inside the nested function to allow modification across recursive calls
Algorithm Steps:
-
Define the DFS function: Create a recursive helper function
dfs(root)
that will traverse the tree. -
Base Case: If the current node is
None
, simply return. This handles leaf nodes' children and empty trees. -
Reverse In-order Traversal:
- First, recursively traverse the right subtree:
dfs(root.right)
- Then, process the current node:
- Add the current node's value to the running sum:
s += root.val
- Update the node's value to the new sum:
root.val = s
- Add the current node's value to the running sum:
- Finally, recursively traverse the left subtree:
dfs(root.left)
- First, recursively traverse the right subtree:
-
Start the traversal: Call
dfs(root)
to begin the process from the root node. -
Return the modified tree: Return the root of the transformed tree.
Why this works:
- By visiting the right subtree first, we ensure we process all nodes with greater values before the current node
- The running sum
s
accumulates all values as we traverse, so when we update a node,s
contains the sum of all greater values plus the current value - The left subtree is visited last because all its values are smaller than the current node
Time Complexity: O(n)
where n
is the number of nodes, as we visit each node exactly once.
Space Complexity: O(h)
where h
is the height of the tree, due to the recursive call stack. In the worst case (skewed tree), this could be O(n)
.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small BST example to see how the reverse in-order traversal transforms it into a Greater Tree.
Initial BST:
4 / \ 2 5 / 1
We'll trace through the algorithm step by step, tracking our running sum s
(initially 0).
Step 1: Start at root (4)
- Call
dfs(4)
- First, we need to traverse the right subtree before processing node 4
Step 2: Traverse right to node 5
- Call
dfs(5)
- Node 5 has no right child, so
dfs(None)
returns immediately - Process node 5:
s = 0 + 5 = 5
- Update node 5's value to 5
- Node 5 has no left child, so
dfs(None)
returns immediately - Return to node 4
Step 3: Process node 4
- Now we can process node 4:
s = 5 + 4 = 9
- Update node 4's value to 9
- Next, traverse the left subtree
Step 4: Traverse left to node 2
- Call
dfs(2)
- Node 2 has no right child, so
dfs(None)
returns immediately - Process node 2:
s = 9 + 2 = 11
- Update node 2's value to 11
- Traverse left to node 1
Step 5: Process node 1
- Call
dfs(1)
- Node 1 has no right child, so
dfs(None)
returns immediately - Process node 1:
s = 11 + 1 = 12
- Update node 1's value to 12
- Node 1 has no left child, so
dfs(None)
returns immediately - Return to node 2, then to node 4
Final Greater Tree:
9 / \ 11 5 / 12
Verification:
- Node 5: Original value 5 + (no greater values) = 5 ✓
- Node 4: Original value 4 + (5) = 9 ✓
- Node 2: Original value 2 + (4 + 5) = 11 ✓
- Node 1: Original value 1 + (2 + 4 + 5) = 12 ✓
The traversal order was: 5 → 4 → 2 → 1 (descending), and each node was updated with the sum of itself plus all previously visited (greater) values.
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 convertBST(self, root: TreeNode) -> TreeNode:
10 """
11 Convert a Binary Search Tree to a Greater Sum Tree where each node's value
12 is updated to the sum of all values greater than or equal to the node's original value.
13
14 Args:
15 root: The root of the binary search tree
16
17 Returns:
18 The root of the modified tree (same tree structure, updated values)
19 """
20
21 def reverse_inorder_traversal(node: TreeNode) -> None:
22 """
23 Perform reverse in-order traversal (right -> root -> left) to process nodes
24 in descending order. Update each node's value with the running sum.
25
26 Args:
27 node: Current node being processed
28 """
29 nonlocal running_sum
30
31 # Base case: if node is None, return
32 if node is None:
33 return
34
35 # Traverse right subtree first (larger values in BST)
36 reverse_inorder_traversal(node.right)
37
38 # Process current node: add its value to running sum and update the node
39 running_sum += node.val
40 node.val = running_sum
41
42 # Traverse left subtree (smaller values in BST)
43 reverse_inorder_traversal(node.left)
44
45 # Initialize running sum to keep track of cumulative sum
46 running_sum = 0
47
48 # Start the traversal from root
49 reverse_inorder_traversal(root)
50
51 return root
52
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 larger nodes
18 private int runningSum;
19
20 /**
21 * Converts a Binary Search Tree to a Greater Sum Tree where each node's value
22 * is updated to the sum of all nodes with values 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 convertBST(TreeNode root) {
28 performReverseInorderTraversal(root);
29 return root;
30 }
31
32 /**
33 * Performs a reverse inorder traversal (right -> node -> left) to visit nodes
34 * in descending order and update each node's value with the running sum.
35 *
36 * @param node The current node being processed
37 */
38 private void performReverseInorderTraversal(TreeNode node) {
39 // Base case: if node is null, return
40 if (node == null) {
41 return;
42 }
43
44 // Traverse the right subtree first (larger values in BST)
45 performReverseInorderTraversal(node.right);
46
47 // Update running sum with current node's value
48 runningSum += node.val;
49
50 // Update current node's value to the running sum
51 node.val = runningSum;
52
53 // Traverse the left subtree (smaller values in BST)
54 performReverseInorderTraversal(node.left);
55 }
56}
57
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 binary search tree
20 * @return The root of the modified tree
21 */
22 TreeNode* convertBST(TreeNode* root) {
23 // Perform reverse in-order traversal to accumulate sum
24 reverseInOrderTraversal(root);
25 return root;
26 }
27
28private:
29 int cumulativeSum = 0; // Running sum of all visited nodes
30
31 /**
32 * Performs a reverse in-order traversal (right -> node -> left)
33 * to visit nodes in descending order and update their values
34 *
35 * @param node Current node being processed
36 */
37 void reverseInOrderTraversal(TreeNode* node) {
38 // Base case: if node is null, return
39 if (!node) {
40 return;
41 }
42
43 // First, traverse the right subtree (larger values in BST)
44 reverseInOrderTraversal(node->right);
45
46 // Process current node: add its value to cumulative sum
47 cumulativeSum += node->val;
48
49 // Update current node's value to the cumulative sum
50 node->val = cumulativeSum;
51
52 // Finally, traverse the left subtree (smaller values in BST)
53 reverseInOrderTraversal(node->left);
54 }
55};
56
1/**
2 * Definition for a binary tree node.
3 */
4interface TreeNode {
5 val: number;
6 left: TreeNode | null;
7 right: TreeNode | null;
8}
9
10/**
11 * Converts a Binary Search Tree to a Greater Sum Tree
12 * where each node's value is replaced by the sum of all values greater than or equal to it.
13 *
14 * @param root - The root node of the binary search tree
15 * @returns The root of the modified tree (same reference, modified in-place)
16 */
17function convertBST(root: TreeNode | null): TreeNode | null {
18 // Running sum to accumulate values during traversal
19 let runningSum: number = 0;
20
21 /**
22 * Performs reverse in-order traversal (right -> node -> left)
23 * to visit nodes in descending order and update their values
24 *
25 * @param node - Current node being processed
26 */
27 function traverseAndUpdate(node: TreeNode | null): void {
28 // Base case: if node is null, return
29 if (!node) {
30 return;
31 }
32
33 // Traverse right subtree first (larger values in BST)
34 traverseAndUpdate(node.right);
35
36 // Update running sum with current node's value
37 runningSum += node.val;
38
39 // Replace current node's value with the running sum
40 node.val = runningSum;
41
42 // Traverse left subtree (smaller values in BST)
43 traverseAndUpdate(node.left);
44 }
45
46 // Start the traversal from root
47 traverseAndUpdate(root);
48
49 return root;
50}
51
Time and Space Complexity
Time Complexity: O(n)
where n
is the number of nodes in the binary search tree. The algorithm performs a modified in-order traversal (right-root-left) that visits each node exactly once. Each visit involves a constant amount of work (adding to the sum and updating the node value), so the total time complexity is linear with respect to the number of nodes.
Space Complexity: O(h)
where h
is the height of the binary search tree. The space complexity comes from the recursive call stack used during the depth-first search traversal. In the worst case (a skewed tree), the height could be O(n)
, making the space complexity O(n)
. In the best case (a balanced tree), the height would be O(log n)
, making the space complexity O(log n)
. The additional space used for the variable s
is O(1)
and doesn't affect the overall space complexity.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Using a Class Variable Instead of Nonlocal
A common mistake is trying to use a class variable to maintain the running sum, which can cause issues with multiple test cases or concurrent calls.
Incorrect approach:
class Solution:
def __init__(self):
self.running_sum = 0 # Class variable - persists between calls!
def convertBST(self, root: TreeNode) -> TreeNode:
def dfs(node):
if not node:
return
dfs(node.right)
self.running_sum += node.val # Using class variable
node.val = self.running_sum
dfs(node.left)
# Problem: running_sum might not be 0 if method called multiple times
dfs(root)
return root
Solution: Always reset the running sum or use a local variable with nonlocal
:
def convertBST(self, root: TreeNode) -> TreeNode:
running_sum = 0 # Local variable
def dfs(node):
nonlocal running_sum # Properly access outer scope variable
if not node:
return
dfs(node.right)
running_sum += node.val
node.val = running_sum
dfs(node.left)
dfs(root)
return root
2. Wrong Traversal Order
Using standard in-order traversal (left → root → right) instead of reverse in-order traversal will produce incorrect results.
Incorrect approach:
def convertBST(self, root: TreeNode) -> TreeNode:
running_sum = 0
def dfs(node):
nonlocal running_sum
if not node:
return
dfs(node.left) # Wrong: visiting smaller values first!
running_sum += node.val
node.val = running_sum
dfs(node.right) # Larger values visited last
dfs(root)
return root
This would accumulate smaller values into larger ones, which is the opposite of what we want.
3. Forgetting to Update the Node Value
Sometimes developers correctly maintain the running sum but forget to actually update the node's value.
Incorrect approach:
def dfs(node):
nonlocal running_sum
if not node:
return
dfs(node.right)
running_sum += node.val # Only updating sum
# Missing: node.val = running_sum
dfs(node.left)
4. Creating a New Tree Instead of Modifying In-Place
The problem typically expects the original tree to be modified in-place, but some might try to create a new tree structure.
Incorrect (and inefficient) approach:
def convertBST(self, root: TreeNode) -> TreeNode:
if not root:
return None
# Creating new nodes instead of modifying existing ones
new_root = TreeNode()
# ... complex logic to rebuild tree
return new_root # Wrong: should return modified original root
5. Not Handling Edge Cases
Failing to handle edge cases like empty trees or single-node trees.
Better approach with explicit edge case handling:
def convertBST(self, root: TreeNode) -> TreeNode:
# Explicitly handle empty tree (though the recursive solution handles this naturally)
if not root:
return root
running_sum = 0
def dfs(node):
nonlocal running_sum
if not node:
return
dfs(node.right)
running_sum += node.val
node.val = running_sum
dfs(node.left)
dfs(root)
return root
Best Practice Solution:
Use a local variable with nonlocal
keyword, perform reverse in-order traversal, and modify nodes in-place. This ensures the solution is self-contained, correct, and efficient.
What is the best way of checking if an element exists in an unsorted array once in terms of time complexity? Select the best that applies.
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!