Given an integer array nums and two integers lower and upper, return the number of range sums that lie in[lower, upper]inclusive.
Range sum S(i, j) is defined as the sum of the elements in nums between indices i and j inclusive, where i <= j.
OR
You are given an integer array nums[] of size n, and two integers lower and upper. Your task is to find the number of non-empty contiguous subarrays whose sums lie within the range [lower, upper], inclusive.
The naive approach involves iterating through all possible subarrays and calculating their sums. For each subarray, we check if the sum lies within the range [lower, upper]. This approach has a time complexity of O(n²) due to the nested iterations over the array.
classSolution {
publicintcountRangeSum(int[] nums, int lower, int upper) {
int n = nums.length;
int count = 0;
// Iterate over all starting indices of subarraysfor (int i = 0; i < n; i++) {
int currentSum = 0; // Variable to store the sum of the current subarray// Iterate over all ending indices for the subarray starting at index ifor (int j = i; j < n; j++) {
currentSum += nums[j]; // Add the current element to the subarray's sum// Check if the sum lies within the range [lower, upper]if (currentSum >= lower && currentSum <= upper) {
count++; // Increment count if the sum is valid }
}
}
return count;
}
publicstaticvoidmain(String[] args) {
Solution solution =new Solution();
// Example 1int[] nums1 = { 1, 2, 3 };
System.out.println(solution.countRangeSum(nums1, 3, 5)); // Output: 3// Example 2int[] nums2 = { -2, 5, -1 };
System.out.println(solution.countRangeSum(nums2, -1, 2)); // Output: 4 }
}
classSolution:
defcountRangeSum(self, nums, lower, upper):
n = len(nums)
count =0# Iterate over all starting indices of subarraysfor i in range(n):
current_sum =0# Variable to store the sum of the current subarray# Iterate over all ending indices for the subarray starting at index ifor j in range(i, n):
current_sum += nums[j] # Add the current element to the subarray's sum# Check if the sum lies within the range [lower, upper]if lower <= current_sum <= upper:
count +=1# Increment count if the sum is validreturn count
# Example usagesolution = Solution()
# Example 1nums1 = [1, 2, 3]
print(solution.countRangeSum(nums1, 3, 5)) # Output: 3# Example 2nums2 = [-2, 5, -1]
print(solution.countRangeSum(nums2, -1, 2)) # Output: 4
How we can improve? Think that if we were asked for contiguous subarray with sum = a then how would we would have solved it?
To improve the solution, let’s consider how we would solve the problem if we were asked to find the number of contiguous subarrays with a sum equal to a. The cumulative sum (cumsum) can be computed while iterating over the array from lower to upper. At each position j, we would check whether there is an earlier position i < j such that the sum of the subarray from i to j equals a. This can be done using cumsum[j] - cumsum[i-1] = a, which simplifies to cumsum[i-1] = cumsum[j] - a. By caching the cumulative sums as we traverse the array, we can efficiently determine if (cumsum[j] - a) has already been seen.
To extend this idea for the range [a, b] instead of a single value, we can use a TreeSet (or a Balanced Binary Search Tree). The O(n log n) solution involves tracking the cumulative sums in a sorted structure. At each position i, if the number itself is within the range [a, b], it is valid. Otherwise, if the cumulative sum at position i falls in the range [a, b], there is a subarray summing to the range from the start to the current position.
To account for subarrays between earlier positions and the current position, using the difference cumsum[j] - S helps check if a subarray from an earlier index sums to S. This can be efficiently tracked using a hash map. For checking the range [a, b], we use a TreeSet to store all cumulative sums seen so far. The number of valid subarrays ending at i can be found using the subset count corresponding to (cumsum[i] - b) and (cumsum[i] - a).
The TreeSet computes subsets efficiently using its subset operations (subSet). For each position, we increment the count by the size of the subset that falls within the range. The implementation ensures efficient operations by avoiding expensive calls like size().
Recall the problem where we counted the number of smaller elements on the upper of each element. Read more here: Count Inversions - Count Smaller on Right. The task involved counting occurrences of x[j] < x[i], for j > i, or equivalently, x[j] - x[i] < 0 for j > i. In the range sum problem, the goal is to count occurrences where a ≤ (x[j] - x[i]) ≤ b for j > i.
This problem can be solved in a similar way to the “count smaller elements on upper” problem, using merge sort. The solution has a complexity of O(n log n) and counts the valid ranges during the merge step. In the merge stage, the lower half [start, mid) and upper half [mid, end) are already sorted. Then, for each index i in the lower half, we locate two indices sl and su in the upper half:
sl marks the first index where S[j] - S[i] > upper.
su marks the first index where S[k] - S[i] ≥ lower.
The subarray sums within [lower, upper] are counted as su - sl. To merge the arrays, we use an auxiliary array merged[].