Description:
Given an integer array nums, 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 (i ≤ j), inclusive.
Note:
A naive algorithm of O(n2) is trivial. You MUST do better than that.
Example:
Given nums = [-2, 5, -1], lower = -2, upper = 2,
Return 3.
The three ranges are : [0, 0], [2, 2], [0, 2] and their respective sums are: -2, -1, 2.
问题描述:
给定一个整数数组,返回在[lower,upper]范围的所有区间和的个数。
区间和S(i, j)为数组在[i,j]范围的元素之和
问题分析:
两种解法,分而治之和二分索引树.如果想完整的弄清楚这两种算法该怎么做,可以看看这个链接;
https://leetcode.com/problems/count-of-range-sum/discuss/78006/Summary-of-the-Divide-and-Conquer-based-and-Binary-Indexed-Tree-based-solutions
如果对二分索引树不熟悉的话,可以看看这个链接:
https://www.geeksforgeeks.org/binary-indexed-tree-or-fenwick-tree-2/
解法1(分而治之):
/*
思路概述如下:
令O(0, n - 1),表示原问题的解
mid = 0 + (n - 1) / 2,那么原问题可以分解为:
O(0, n - 1) = O(0, mid) + O(mid + 1, n - 1) + C
其中C表示i位于[0, mid],j位于[mid + 1, n - 1]的解
那么对于左半边可以维护一个后缀数组(与前缀数组的顺序相反),右半边可以维护一个前缀数组,将
前缀数组排序后,通过二分查找确定边界范围
*/
class Solution {
public int countRangeSum(int[] nums, int lower, int upper) {
if(nums == null || nums.length == 0 || lower > upper) return 0;
return mergeSort(nums, 0, nums.length - 1, lower, upper);
}
public int mergeSort(int[] nums, int l, int r, int lower, int upper){
if(l == r) return lower <= nums[l] && nums[l] <= upper ? 1 : 0;
int m = l + (r - l) / 2;
//前缀数组
long[] arr = new long[r - m];
long sum = 0;
for(int i = m + 1;i <= r;i++){
sum += nums[i];
arr[i - m - 1] = sum;
}
//对前缀数组排序
Arrays.sort(arr);
sum = 0;
int count = 0;
for(int i = m;i >= l;i--){
sum += nums[i];
//通过二分查找确定范围
count += findIndex(arr, upper - sum + 0.5) - findIndex(arr, lower - sum - 0.5);
}
//将原问题分解为子问题,分而治之
return mergeSort(nums, l, m, lower, upper) + mergeSort(nums, m + 1, r, lower, upper) + count;
}
public int findIndex(long[] arr, double val){
int left = 0, right = arr.length - 1, mid = -1;
while(left <= right){
mid = left + (right - left) / 2;
if(arr[mid] > val){
right = mid - 1;
}else{
left = mid + 1;
}
}
return left;
}
}
解法2(归并排序):
class Solution {
public int countRangeSum(int[] nums, int lower, int upper) {
int n = nums.length;
long[] sums = new long[n + 1];
for (int i = 0; i < n; ++i)
sums[i + 1] = sums[i] + nums[i];
return countWhileMergeSort(sums, 0, n + 1, lower, upper);
}
private int countWhileMergeSort(long[] sums, int start, int end, int lower, int upper) {
if (end - start <= 1) return 0;
int mid = (start + end) / 2;
int count = countWhileMergeSort(sums, start, mid, lower, upper)
+ countWhileMergeSort(sums, mid, end, lower, upper);
int j = mid, k = mid, t = mid;
long[] cache = new long[end - start];
for (int i = start, r = 0; i < mid; ++i, ++r) {
while (k < end && sums[k] - sums[i] < lower) k++;
while (j < end && sums[j] - sums[i] <= upper) j++;
while (t < end && sums[t] < sums[i]) cache[r++] = sums[t++];
cache[r] = sums[i];
count += j - k;
}
System.arraycopy(cache, 0, sums, start, t - start);
return count;
}
}
解法3(二分索引树):
class Solution {
public int countRangeSum(int[] nums, int lower, int upper) {
long[] sum = new long[nums.length + 1];
long[] cand = new long[3 * sum.length + 1];
int index = 0;
cand[index++] = sum[0];
cand[index++] = lower + sum[0] - 1;
cand[index++] = upper + sum[0];
for (int i = 1; i < sum.length; i++) {
sum[i] = sum[i - 1] + nums[i - 1];
cand[index++] = sum[i];
cand[index++] = lower + sum[i] - 1;
cand[index++] = upper + sum[i];
}
cand[index] = Long.MIN_VALUE; // avoid getting root of the binary indexed tree when doing binary search
Arrays.sort(cand);
int[] bit = new int[cand.length];
// build up the binary indexed tree with only elements from the prefix array "sum"
for (int i = 0; i < sum.length; i++) {
addValue(bit, Arrays.binarySearch(cand, sum[i]), 1);
}
int count = 0;
for (int i = 0; i < sum.length; i++) {
// get rid of visited elements by adding -1 to the corresponding tree nodes
addValue(bit, Arrays.binarySearch(cand, sum[i]), -1);
// add the total number of valid elements with upper bound (upper + sum[i])
count += query(bit, Arrays.binarySearch(cand, upper + sum[i]));
// minus the total number of valid elements with lower bound (lower + sum[i] - 1)
count -= query(bit, Arrays.binarySearch(cand, lower + sum[i] - 1));
}
return count;
}
private void addValue(int[] bit, int index, int value) {
while (index < bit.length) {
bit[index] += value;
index += index & -index;
}
}
private int query(int[] bit, int index) {
int sum = 0;
while (index > 0) {
sum += bit[index];
index -= index & -index;
}
return sum;
}
}