Leetcode 938. Range Sum of BST

Problem Explanation

Given a binary search tree (BST) and two integers (L, R), the goal of this problem is to find the sum of node values within the BST that are between the range of L and R (inclusive). The BST is guaranteed to have unique values.

For example, consider the following BST and the range (L = 6, R = 10):

1
2
3    10
4   /  \
5  5    15
6 / \  /  \
73   7 13  18
8 \
9  1
10   \
11    6

In this case, the nodes with values between 6 and 10 are 6, 7, and 10. Therefore, the output would be the sum of these nodes, which is 6 + 7 + 10 = 23.

Solution Approach

Due to the properties of BST (left child < parent and parent < right child), we can use a Depth-First-Search (DFS) to traverse the tree avoiding unnecessary branches. When the current node value is less than L, there's no need to check the left subtree; when the current node value is greater than R, there's no need to check the right subtree. By doing this, we save some computational cost.

Solution in Python

1
2python
3class TreeNode:
4    def __init__(self, x):
5        self.val = x
6        self.left = None
7        self.right = None
8
9class Solution:
10    def rangeSumBST(self, root: TreeNode, L: int, R: int) -> int:
11        if root is None:
12            return 0
13        if root.val < L:
14            return self.rangeSumBST(root.right, L, R)
15        if root.val > R:
16            return self.rangeSumBST(root.left, L, R)
17        return root.val + self.rangeSumBST(root.left, L, R) + self.rangeSumBST(root.right, L, R)

Solution in Java

1
2java
3class Solution {
4    public int rangeSumBST(TreeNode root, int L, int R) {
5        if (root == null) {
6            return 0;
7        }
8        if (root.val < L) {
9            return rangeSumBST(root.right, L, R);
10        }
11        if (root.val > R) {
12            return rangeSumBST(root.left, L, R);
13        }
14        return root.val + rangeSumBST(root.left, L, R) + rangeSumBST(root.right, L, R);
15    }
16}

Solution in JavaScript

1
2javascript
3var rangeSumBST = function(root, L, R) {
4    if(!root) return 0;
5    if(root.val < L) return rangeSumBST(root.right, L, R);
6    if(root.val > R) return rangeSumBST(root.left, L, R);
7    return root.val + rangeSumBST(root.left, L, R) + rangeSumBST(root.right, L, R);
8};

Solution in C++

1
2c++
3class Solution {
4public:
5    int rangeSumBST(TreeNode* root, int L, int R) {
6        if (!root) return 0;
7        if (root->val < L) return rangeSumBST(root->right, L, R);
8        if (root->val > R) return rangeSumBST(root->left, L, R);
9        return root->val + rangeSumBST(root->left, L, R) + rangeSumBST(root->right, L, R);
10    }
11};

Solution in C#

1
2csharp
3public class Solution {
4    public int RangeSumBST(TreeNode root, int L, int R) {
5        if (root == null) return 0;
6        if (root.val < L) return RangeSumBST(root.right, L, R);
7        if (root.val > R) return RangeSumBST(root.left, L, R);
8        return root.val + RangeSumBST(root.left, L, R) + RangeSumBST(root.right, L, R);
9    }
10}

In all these solutions, we recursively iterate through the BST and if the current node's value is lesser than the lower limit (L), we move to right sub-tree as there could possible nodes that fall within the range. Conversely, if current node's value is more than the upper limit (R), we move to left sub-tree. If the current node's value falls within the range, we add it to the sum and check both right and left sub-trees for other possible values.# Time Complexity Analysis

All of the above solutions have the same time complexity and efficiency due to their similar logic and approach.

The worst-case scenario would be if all of the node values fall within the range (L, R) - which would require visiting every node in the tree. Given that a binary tree may have n nodes, the worst-case time complexity of the problem would be O(n).

However, due to the nature of binary search trees, and our specific approach to solving this problem, we can often achieve a much better average time complexity. Particularly in balanced trees, our ability to completely ignore significant parts of the tree based on the value of a single node, allows us to achieve an average time complexity of O(log n). This is because we divide our problem size by approximately 2 for each level of depth we traverse in the tree.

It is important to note that these time complexities apply to all the language implementations of the solution (Python, JavaScript, Java, C++, and C#), as the efficiency of the solution predominantly relies on the logic and data structure used, and not the specific language syntax or quirks.

The space complexity of these solutions is also O(n) in the worst case, where n is the height of the tree, essentially the maximum depth of recursive calls.

Conclusion

In conclusion, this problem acts as a good example of how understanding the properties and constraints of your data structures can significantly improve the efficiency of your solutions. By leveraging the characteristics of a binary search tree, we managed to construct concise and efficient solutions to a seemingly complex problem across different programming languages.

Remember, the key to writing good code is understanding your data structures, thinking through your problem logically, and optimizing your solution wherever you can. Happy coding!


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫