LeetCode 327 Count of Range Sum

求数组内有多少段区间和落在low和high的闭区间内,要求时间复杂度小于O(n^2)
  1. 先求出累计和sum,如果没有时间复杂度的限制的话,应该遍历,用后面的sum[j]减去前面的sum[i],只要落在区间内就+1
  2. 但是要限制复杂度,所以我们想到利用排序来减少一些不必要的重复。可以利用分治思想,先计算出端点在每个子区间内的数量,然后再计算端点落在相邻不同区间的数量。在分治的同时,完成归并排序,不破坏两个相邻区间内数先后关系(必须要后面减前面),同时两个区间内部变为有序,使得搜索右端点不需要从新的左端点开始,而是从前面的位置继续即可。

C++ Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Solution {
public:
int mergeSort(vector<long long> &sum, int lower, int upper, int left, int right) {
if(left+1 >= right) {
return 0;
}
int mid = (left + right) >> 1;
int cnt = 0;
cnt += mergeSort(sum, lower, upper, left, mid);
cnt += mergeSort(sum, lower, upper, mid, right);
int lower_pos = mid, upper_pos = mid;
for(int i = left; i < mid; ++i) {
while(lower_pos < right && sum[lower_pos] - sum[i] < lower) ++lower_pos;
while(upper_pos < right && sum[upper_pos] - sum[i] <= upper) ++upper_pos;
cnt += (upper_pos - lower_pos);
}

vector<long long> tmp(right - left);
for(int i = left; i < right; ++i) {
tmp[i - left] = sum[i];
}
int i = 0, j = mid - left;
int pos = left;
while(i < mid - left || j < right - left) {
if(i >= mid - left) {
sum[pos++] = tmp[j++];
}else if(j >= right - left) {
sum[pos++] = tmp[i++];
}else if(tmp[i] < tmp[j]) {
sum[pos++] = tmp[i++];
}else {
sum[pos++] = tmp[j++];
}
}
// inplace_merge(sum.begin()+left, sum.begin()+mid, sum.begin()+right);
return cnt;
}

int countRangeSum(vector<int>& nums, int lower, int upper) {
int sz = nums.size();
vector<long long> sum(sz + 1, 0);
for(int i = 1; i < sz + 1; ++i) {
sum[i] = sum[i-1] + nums[i-1];
}
int ans = mergeSort(sum, lower, upper, 0, sz + 1);
return ans;
}
};