938. Range Sum of BST - Explanation
Description
You are given the root node of a binary search tree and two integers low and high, return the sum of values of all nodes with a value in the inclusive range [low, high].
Example 1:
Input: root = [5,3,8,1,4,7,9,null,2], low = 3, high = 8
Output: 27Explanation: Nodes 5, 3, 8, 4 and 7 are in the range [3, 8]. 5 + 3 + 8 + 4 + 7 = 27.
Example 2:
Input: root = [4,3,5,2,null], low = 2, high = 4
Output: 9Explanation: Nodes 4, 3, and 2 are in the range [2, 4]. 4 + 3 + 2 = 9.
Constraints:
1 <= The number of nodes in the tree <= 20,000.1 <= Node.val, low, high <= 100,000- All
Node.valare unique.
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Binary Search Tree (BST) Properties - Understanding that left subtree values are smaller and right subtree values are larger than the current node
- Depth First Search (DFS) - Recursive tree traversal to visit all nodes and accumulate values
- Tree Pruning - Using BST ordering to skip entire subtrees that cannot contain values in the target range
1. Depth First Search
Intuition
We traverse the entire tree and sum up values that fall within the given range. For each node, we check if its value is between low and high, adding it to our sum if so. We then recursively process both children. This approach visits every node regardless of the BST property.
Algorithm
- Base case: if the node is
null, return0. - Check if the current node's value is within
[low, high]. If yes, include it in the result. - Recursively compute the range sum for the left subtree.
- Recursively compute the range sum for the right subtree.
- Return the sum of all contributions.
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def rangeSumBST(self, root: Optional[TreeNode], low: int, high: int) -> int:
if not root:
return 0
res = root.val if low <= root.val <= high else 0
res += self.rangeSumBST(root.left, low, high)
res += self.rangeSumBST(root.right, low, high)
return resTime & Space Complexity
- Time complexity:
- Space complexity: for recursion stack.
2. Depth First Search (Optimal)
Intuition
We can leverage the BST property to prune unnecessary branches. If the current node's value is greater than high, all values in the right subtree are also too large, so we only need to search left. Similarly, if the current value is less than low, we only search right. This eliminates entire subtrees that cannot contribute to the sum.
Algorithm
- Base case: if the node is
null, return0. - If the current value exceeds
high, skip the right subtree and recurse only on the left. - If the current value is below
low, skip the left subtree and recurse only on the right. - Otherwise, the current value is in range. Add it to the sum of both subtree results.
- Return the computed sum.
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def rangeSumBST(self, root: Optional[TreeNode], low: int, high: int) -> int:
if not root:
return 0
if root.val > high:
return self.rangeSumBST(root.left, low, high)
if root.val < low:
return self.rangeSumBST(root.right, low, high)
return (
root.val +
self.rangeSumBST(root.left, low, high) +
self.rangeSumBST(root.right, low, high)
)Time & Space Complexity
- Time complexity:
- Space complexity: for recursion stack.
3. Iterative DFS
Intuition
The recursive solution can be converted to an iterative one using an explicit stack. We maintain the same pruning logic: only push children onto the stack if they might contain values in range. This avoids function call overhead while preserving the efficiency of the optimal approach.
Algorithm
- Initialize a stack with the root node and a result variable set to
0. - While the stack is not empty:
- Pop a node. If
null, continue. - If the node's value is in range
[low, high], add it to the result. - If the node's value is greater than
low, push the left child. - If the node's value is less than
high, push the right child.
- Pop a node. If
- Return the accumulated result.
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def rangeSumBST(self, root: Optional[TreeNode], low: int, high: int) -> int:
stack = [root]
res = 0
while stack:
node = stack.pop()
if not node:
continue
if low <= node.val <= high:
res += node.val
if node.val > low:
stack.append(node.left)
if node.val < high:
stack.append(node.right)
return resTime & Space Complexity
- Time complexity:
- Space complexity:
Common Pitfalls
Not Leveraging BST Property for Pruning
Treating the tree as a regular binary tree and visiting every node wastes time. Since it is a BST, when the current node's value exceeds high, the entire right subtree contains only larger values and can be skipped. Similarly, when the value is below low, the left subtree can be skipped. Failing to prune these branches results in O(n) time even when only a small portion of nodes fall within the range.
Incorrect Range Boundary Checks
Using strict inequalities (< and >) instead of inclusive checks (<= and >=) when determining whether to include the current node's value causes off-by-one errors. The condition should be low <= node.val <= high to include boundary values. Missing boundary values leads to incorrect sums when low or high exactly match node values in the tree.
Sign in to join the discussion