327. Count of Range Sum
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:
Input: nums = [-2,5,-1], lower = -2, upper = 2,
Output: 3
Explanation: The three ranges are : [0,0], [2,2], [0,2] and their respective sums are: -2, -1, 2.
方法1:presum + map
思路:
和之前的寻找sum k subarray一样,只是变成了找到一个sum in [lower, upper]的数量。那么如果不能assume都是正数的话, presum并不是一个有序数列,这个计数仍然只能通过全扫描来完成。为了优化,可以强行将presum sort,记录成一个map,这样每次可以用二分内置函数lower/upper_bound来找到范围。这样再用distance可以直接求算这两个iterator之间元素的距离。distance(lb, ub)用于set,accumulate + 自定义(operator+)的方式可以累加map::iterator之间的val。
易错点:
Complexity
Time complexity: O(nlogn)
Space complexity: O(n)
class Solution {
public:
int countRangeSum(vector<int>& nums, int lower, int upper) {
map<long long, int> presum;
presum[0] = 1;
long long sum = 0;
int res = 0;
for (auto num: nums) {
sum += num;
auto lb = presum.lower_bound(sum - upper);
auto ub = presum.upper_bound(sum - lower);
res += accumulate(lb, ub, 0, [](int v, const map<long long, int>::value_type& pair){
return v + pair.second;
// if we want to sum the first item, change it to
// return v + pair.first;
});
presum[sum] ++;
}
return res;
}
};
方法2:merge sort
思路:
上面的方法用到了map来自动排序,这里只是实现了merge sort 来对presum vector进行排序。
class Solution {
public:
int mergeSort(vector<long>& sum, int lower, int upper, int low, int high)
{
if(high-low <= 1) return 0;
int mid = (low+high)/2, m = mid, n = mid, count =0;
count =mergeSort(sum,lower,upper,low,mid) +mergeSort(sum,lower,upper,mid,high);
for(int i = low;i < mid; ++i){
auto m = lower_bound(sum.begin()+mid,sum.begin()+high,sum[i] + lower);
auto n = upper_bound(sum.begin()+mid,sum.begin()+high,sum[i] + upper);
count += n-m;
}
inplace_merge(sum.begin()+low, sum.begin()+mid, sum.begin()+high);
return count;
}
int countRangeSum(vector<int>& nums, int lower, int upper) {
int len = nums.size();
vector<long> sum(len + 1, 0);
for(int i =0; i< len; i++) sum[i+1] = sum[i]+nums[i];
return mergeSort(sum, lower, upper, 0, len+1);
}
};
不用upper和lower_bound的做法
class Solution {
public:
int mergeSort(vector<long>& sum, int lower, int upper, int low, int high)
{
if(high-low <= 1) return 0;
int mid = (low+high)/2, m = mid, n = mid, count =0;
count = mergeSort(sum,lower,upper,low,mid) + mergeSort(sum,lower,upper,mid,high);
for(int i =low; i< mid; i++)
{
while(m < high && sum[m] - sum[i] < lower) m++;
while(n < high && sum[n] - sum[i] <= upper) n++;
count += n - m;
}
inplace_merge(sum.begin()+low, sum.begin()+mid, sum.begin()+high);
return count;
}
int countRangeSum(vector<int>& nums, int lower, int upper) {
int len = nums.size();
vector<long> sum(len + 1, 0);
for(int i =0; i< len; i++) sum[i+1] = sum[i]+nums[i];
return mergeSort(sum, lower, upper, 0, len+1);
}
};
方法3: BIT / Fenwick Tree
hrwhisper: https://www.hrwhisper.me/leetcode-count-of-range-sum/
思路:
Fenwich Tree 所帮忙解决的是在logn时间内找到presum的范围。
typedef long long LL;
class FenwickTree {
vector<int> sum_array;
int n;
inline int lowbit(int x) {
return x & -x;
}
public:
FenwickTree(int n) :n(n), sum_array(n + 1, 0) {}
void add(int x, int val) {
while (x <= n) {
sum_array[x] += val;
x += lowbit(x);
}
}
int sum(int x) {
int res = 0;
while (x > 0) {
res += sum_array[x];
x -= lowbit(x);
}
return res;
}
};
class Solution {
public:
int countRangeSum(vector<int>& nums, int lower, int upper) {
if (nums.size() == 0) return 0;
vector<LL> sum_array (nums.size() * 3,0);
LL sum = 0;
for (int i = 0; i < nums.size(); i++) {
sum += nums[i];
sum_array[i * 3] = sum;
sum_array[i * 3 + 1] = sum + lower - 1;
sum_array[i * 3 + 2] = sum + upper;
}
sum_array.push_back(upper);
sum_array.push_back(lower - 1);
unordered_map<LL, int> index;
sort(sum_array.begin(), sum_array.end());
auto end = unique(sum_array.begin(), sum_array.end());
auto it = sum_array.begin();
for (int i = 1; it != end;i++,it++) {
index[*it] = i;
}
FenwickTree tree(index.size());
int ans = 0;
for (int i = nums.size() - 1; i >= 0; i--) {
tree.add(index[sum],1);
sum -= nums[i];
ans += tree.sum(index[upper + sum]) - tree.sum(index[lower + sum -1]);
}
return ans;
}
};