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:
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
.
分治法来处理.
0、sum[i]表示前i项的和。这一点是关键,用来计算的是sum[i]
1、 利用归并排序算法的将数组分成左右两边,
2、分别算左右两截的内部的可能的情况。
3、在这两截合并组成 满足的 情况的算法:
循环在第一截中遍历起始点。
在第二截中找结束点,看有多少种满足的情况。找结束点的方式是:
在合并左右数组的时候对于左边数组中的每一个元素在右边数组找到一个范围, 使得在这个范围中的的元素与左边的元素构成的区间和落在[lower, upper]之间. 即在右边数组找到两个边界, 设为m, n, 其中m是在右边数组中第一个使得sum[m]- sum[i] >= lower的位置, n是第一个使得sum[n] - sum[i] > upper的位置, 这样n-m就是与左边元素i所构成的位于[lower,upper]范围的区间个数.
4、再把左右两截范围的sum排序。sum排序是这个算法做下去的关键。
其时间复杂度为O(n log n)
注:
inplace_merge函数的作用和merge函数差不多,只不过是在一个容器中进行归并。函数参数:inplace_merge(first,mid,last,compare);//将[first,mid) 和 [mid,last)这两个区间进行归并成一个有序序列。[first,mid)和[mid,last)都要呈升序或降序排列!
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 ++; //因为是 两截中的 sum 是排好序的 所以可以用差值来计算 结束点的距离
while (n < high && sum[n] - sum[i] <= upper) n ++;
count += n - m;
}
//把两截的sum归并排序。因为这一截内部的已经算完。顺序没有关系了。
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);
}
};
参考:http://blog.csdn.net/qq508618087/article/details/51435944