title: Binary Search Tree to Greater Sum Tree slug: binary-search-tree-to-greater-sum-tree difficulty: medium leetcode_id: 1038 leetcode_url: https://leetcode.com/problems/binary-search-tree-to-greater-sum-tree/ categories: - trees patterns: - dfs - tree-traversal description: | Given the `root` of a Binary Search Tree (BST), convert it to a Greater Tree such that every key of the original BST is changed to the original key plus the sum of all keys greater than the original key in BST. As a reminder, a *binary search tree* is a tree that satisfies these constraints: - 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. constraints: | - `1 <= number of nodes <= 100` - `0 <= Node.val <= 100` - All values in the tree are **unique** examples: - input: "root = [4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]" output: "[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]" explanation: "Each node's value becomes the sum of itself plus all nodes with greater values. For example, node 4 becomes 4+5+6+7+8=30." - input: "root = [0,null,1]" output: "[1,null,1]" explanation: "Node 1 has no greater values so stays 1. Node 0 becomes 0+1=1." explanation: intuition: | The key insight is understanding what "all keys greater than the original key" means in a BST. In a BST, an **inorder traversal** (left → node → right) visits nodes in ascending order. If we **reverse** this traversal order (right → node → left), we visit nodes in **descending order** — from largest to smallest. Think of it like this: imagine you have a sorted list of all node values `[0, 1, 2, 3, 4, 5, 6, 7, 8]`. For each value, you need to add all values to its right. If you traverse from right to left, you can maintain a **running sum** that accumulates as you go. When you visit node with value 8 (the largest), no values are greater, so it stays 8. When you visit 7, you add the running sum (8), making it 15. When you visit 6, you add the running sum (8+7=15), making it 21. And so on. By traversing in reverse-inorder (right → node → left), each node we visit is smaller than all previously visited nodes, so our running sum naturally represents "the sum of all greater values." approach: | We use a **Reverse Inorder Traversal** with a running sum: **Step 1: Understand the traversal order** - Normal inorder: left → node → right (ascending order) - Reverse inorder: right → node → left (descending order) - By visiting nodes from largest to smallest, we can build up the sum incrementally   **Step 2: Maintain a running sum** - `running_sum`: Tracks the sum of all nodes visited so far - Since we visit in descending order, this equals "sum of all greater values" - Initialise to `0` before traversal   **Step 3: Perform reverse inorder traversal** - Recursively visit the right subtree first (larger values) - At the current node: - Add the node's value to `running_sum` - Update the node's value to `running_sum` - Recursively visit the left subtree (smaller values)   **Step 4: Return the modified root** - The tree is modified in-place - Return the original root reference   This approach processes each node exactly once, and the running sum at each node naturally represents the sum of all greater values plus the current value. common_pitfalls: - title: Using Normal Inorder Instead of Reverse description: | A common mistake is performing a normal inorder traversal (left → node → right). This visits nodes in ascending order, which means when you reach a node, you've only seen smaller values — not the greater ones you need to sum. With reverse inorder (right → node → left), you visit larger values first, so your running sum correctly represents "sum of all greater values." wrong_approach: "Normal inorder traversal (left → node → right)" correct_approach: "Reverse inorder traversal (right → node → left)" - title: Forgetting to Update Node Value Before Recursing Left description: | The order of operations matters. You must: 1. Visit right subtree 2. Add current value to running sum 3. Update current node's value 4. Visit left subtree If you update the node after visiting the left subtree, nodes in the left subtree won't include this node's contribution in their sums. - title: Not Modifying In-Place description: | The problem asks you to modify the tree in-place and return the same root. Don't create a new tree or return a different root node. Simply update `node.val = running_sum` at each node during traversal. key_takeaways: - "**Reverse inorder traversal** visits BST nodes in descending order — useful when you need to process from largest to smallest" - "**Running sum pattern**: When accumulating values during traversal, consider which direction gives you the values you need" - "**BST property exploitation**: The structure of a BST provides natural ordering that can be leveraged for efficient solutions" - "This problem is identical to LeetCode 538 (Convert BST to Greater Tree) — recognising equivalent problems is a valuable skill" time_complexity: "O(n). We visit each node exactly once during the reverse inorder traversal." space_complexity: "O(h) where h is the height of the tree. This is the recursion stack depth, which is O(log n) for a balanced tree or O(n) for a skewed tree." solutions: - approach_name: Reverse Inorder Traversal (Recursive) is_optimal: true code: | class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def bst_to_gst(root: TreeNode) -> TreeNode: # Running sum of all nodes visited so far (larger values) running_sum = 0 def reverse_inorder(node: TreeNode) -> None: nonlocal running_sum if not node: return # Visit right subtree first (larger values) reverse_inorder(node.right) # Add current value to running sum, then update node running_sum += node.val node.val = running_sum # Visit left subtree (smaller values) reverse_inorder(node.left) reverse_inorder(root) return root explanation: | **Time Complexity:** O(n) — Each node is visited exactly once. **Space Complexity:** O(h) — Recursion stack depth equals tree height. We traverse the tree in reverse inorder (right → node → left), maintaining a running sum. At each node, we add its value to the sum and update the node to hold this accumulated value. Since we visit larger nodes first, the running sum always represents the sum of all greater values. - approach_name: Iterative with Stack is_optimal: true code: | def bst_to_gst(root: TreeNode) -> TreeNode: running_sum = 0 stack = [] current = root # Iterative reverse inorder: right -> node -> left while stack or current: # Go as far right as possible while current: stack.append(current) current = current.right # Process current node current = stack.pop() running_sum += current.val current.val = running_sum # Move to left subtree current = current.left return root explanation: | **Time Complexity:** O(n) — Each node is visited exactly once. **Space Complexity:** O(h) — Stack stores at most h nodes (tree height). This is the iterative version using an explicit stack. We simulate the recursion by first pushing all right children onto the stack, processing nodes as we pop them, then moving to left children. This avoids recursion overhead but has the same time and space complexity. - approach_name: Morris Traversal is_optimal: true code: | def bst_to_gst(root: TreeNode) -> TreeNode: running_sum = 0 current = root while current: if not current.right: # No right subtree: process node, move left running_sum += current.val current.val = running_sum current = current.left else: # Find inorder predecessor in right subtree # (leftmost node in right subtree) successor = current.right while successor.left and successor.left != current: successor = successor.left if not successor.left: # Create temporary link back to current successor.left = current current = current.right else: # Remove temporary link, process node successor.left = None running_sum += current.val current.val = running_sum current = current.left return root explanation: | **Time Complexity:** O(n) — Each edge is traversed at most twice. **Space Complexity:** O(1) — No recursion stack or explicit stack used. Morris traversal achieves O(1) space by temporarily modifying the tree structure. We create temporary links from nodes back to their "successors" in the reverse inorder. This is an advanced technique that trades code complexity for space efficiency.