questions C

This commit is contained in:
2025-05-25 10:16:13 +01:00
parent c4662f5001
commit 615e3f1291
85 changed files with 16925 additions and 0 deletions

View File

@@ -0,0 +1,224 @@
title: Convert BST to Greater Tree
slug: convert-bst-to-greater-tree
difficulty: medium
leetcode_id: 538
leetcode_url: https://leetcode.com/problems/convert-bst-to-greater-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: |
- `0 <= number of nodes <= 10^4`
- `-10^4 <= Node.val <= 10^4`
- All the values in the tree are **unique**
- `root` is guaranteed to be a valid binary search tree
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 new value equals its original value plus the sum of 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 stays 1 (no greater values). Node 0 becomes 0 + 1 = 1."
explanation:
intuition: |
Imagine you have a sorted list of numbers and you want to replace each number with the sum of itself and all numbers greater than it. You would naturally start from the **largest number** and work backwards, keeping a running total.
The key insight is that a BST has a built-in sorted order: an **in-order traversal** (left → node → right) visits nodes in ascending order. But we need descending order to accumulate sums from largest to smallest.
The solution is beautifully simple: use a **reverse in-order traversal** (right → node → left). This visits nodes from largest to smallest. As we traverse, we maintain a running sum of all values seen so far. Each node's new value becomes this running sum (which includes its original value plus all greater values).
Think of it like this: you're walking through the tree in reverse sorted order, carrying a "cumulative total" that grows with each node you visit. When you reach a node, you add its value to your total, then update the node with this new total.
approach: |
We solve this using **Reverse In-Order Traversal** with a running sum:
**Step 1: Understand the traversal order**
- Standard in-order (left → node → right) gives ascending order
- Reverse in-order (right → node → left) gives **descending order**
- We need descending order to accumulate sums correctly
&nbsp;
**Step 2: Initialise a running sum variable**
- `running_sum`: Set to `0` initially
- This tracks the sum of all nodes visited so far (all values greater than current)
&nbsp;
**Step 3: Perform reverse in-order traversal**
- Recursively visit the **right subtree** first (larger values)
- Process the **current node**: add its value to `running_sum`, then update the node's value to `running_sum`
- Recursively visit the **left subtree** (smaller values)
&nbsp;
**Step 4: Return the modified root**
- The tree is modified in-place during traversal
- Return the original root reference
common_pitfalls:
- title: Using Standard In-Order Traversal
description: |
A common mistake is using left → node → right traversal. This visits nodes in ascending order, but we need descending order to know the sum of greater values before processing each node.
With ascending order, when you visit a node, you don't yet know the sum of all greater values because you haven't visited them yet!
wrong_approach: "Standard in-order traversal (left → node → right)"
correct_approach: "Reverse in-order traversal (right → node → left)"
- title: Two-Pass Solution
description: |
Some might think they need two passes: first to calculate the total sum, then another to update each node. While this works, it's unnecessarily complex.
The reverse in-order traversal elegantly solves this in a single pass because we naturally visit larger values first.
wrong_approach: "First pass to sum all values, second pass to update nodes"
correct_approach: "Single reverse in-order pass with running sum"
- title: Forgetting to Update Running Sum Before Node Value
description: |
The order of operations matters. You must add the current node's value to the running sum **before** updating the node's value, otherwise you lose the original value.
```python
# Wrong order:
node.val = running_sum # Lost original value!
running_sum += node.val # Now adding the wrong value
# Correct order:
running_sum += node.val # Add original value first
node.val = running_sum # Then update node
```
wrong_approach: "Update node value before adding to running sum"
correct_approach: "Add to running sum first, then update node value"
key_takeaways:
- "**Reverse in-order traversal** visits BST nodes in descending order — essential when you need to process from largest to smallest"
- "**Running accumulator pattern**: maintain a running total during traversal when nodes depend on previously visited values"
- "**BST property exploitation**: the sorted nature of BSTs enables elegant single-pass solutions"
- "This problem is identical to LeetCode 1038 (Binary Search Tree to Greater Sum Tree)"
time_complexity: "O(n). Each node is visited exactly once during the traversal."
space_complexity: "O(h) where h is the height of the tree. This is the recursion stack space — O(log n) for a balanced tree, O(n) for a skewed tree."
solutions:
- approach_name: Reverse In-Order 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 convert_bst(root: TreeNode | None) -> TreeNode | None:
# Running sum of all values greater than current node
running_sum = 0
def reverse_inorder(node: TreeNode | None) -> None:
nonlocal running_sum
if not node:
return
# Visit right subtree first (larger values)
reverse_inorder(node.right)
# Process current node: add to 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 visited once.
**Space Complexity:** O(h) — Recursion stack depth equals tree height.
The recursive approach directly implements reverse in-order traversal. We use `nonlocal` to maintain the running sum across recursive calls. The tree is modified in-place.
- approach_name: Reverse In-Order Traversal (Iterative)
is_optimal: true
code: |
def convert_bst(root: TreeNode | None) -> TreeNode | None:
running_sum = 0
stack = []
current = root
# Iterative reverse in-order: right -> node -> left
while stack or current:
# Go as far right as possible
while current:
stack.append(current)
current = current.right
# Process the rightmost unprocessed 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 visited once.
**Space Complexity:** O(h) — Stack stores at most h nodes.
The iterative version uses an explicit stack to simulate recursion. We traverse right as far as possible, then process nodes and move left. This avoids recursion overhead and stack overflow for very deep trees.
- approach_name: Morris Traversal
is_optimal: true
code: |
def convert_bst(root: TreeNode | None) -> TreeNode | None:
running_sum = 0
current = root
while current:
if not current.right:
# No right child: process node, move left
running_sum += current.val
current.val = running_sum
current = current.left
else:
# Find in-order predecessor in right subtree
# (leftmost node of right subtree)
predecessor = current.right
while predecessor.left and predecessor.left != current:
predecessor = predecessor.left
if not predecessor.left:
# Create temporary link back to current
predecessor.left = current
current = current.right
else:
# Remove temporary link, process current node
predecessor.left = None
running_sum += current.val
current.val = running_sum
current = current.left
return root
explanation: |
**Time Complexity:** O(n) — Each edge traversed at most twice.
**Space Complexity:** O(1) — No additional space beyond pointers.
Morris traversal achieves O(1) space by temporarily modifying tree pointers to create "threads" back to ancestors. This is the most space-efficient solution but modifies and restores the tree structure during traversal.