题目: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.
分析:用分治算法,对于
(a0,a1,a2,...,an−1)
,所有可能的解区间是
(a0,a1,a2,...,a(n−1)/2)
和
(a(n−1)/2+1,a(n−1)/2+2,a(n−1)/2+3,...,an−1)
中的解区间以及横跨两部分的解区间。
我们要需要考虑的是如何求解横跨两部分的解区间。因为我们要做到优于
O(n2)
的算法,在递归式中
T(n)=2T(n/2)+f(x)
,
f(x)
至少应该是
O(nlgn)
,这样才能得到
O(nlgn)
的算法。
举个栗子,nums = [-2, 5, -1,7,-4,9],横跨两部分的区间一定至少包括[-1,7],然后向两边进行扩展。如果对于左边n/2-1个数,尝试着与右边的每个数进行匹配,那么 f(x) 会达到 O(n2) 。这里的做法是将右边每一个数到-1的区间和存在另一个数组中,对数组进行 O(nlgn) 的排序,然后对左边n/2-1个数分别进行 O(lgn) 的二分查找。这样使得 f(x) 为 O(nlgn) ,根据Master Theory,整个分治算法的复杂度为 O(nlgn) 。
#include <vector>
#include <algorithm>
#include <iostream>
using std::sort;
using std::cout;
using std::endl;
using std::vector;
class Solution {
public:
int binarySearch(int* nums, int size, int lower, int upper) {
int i = 0, j = size - 1, mid;
int upperBound = -1, lowerBound = -1;
while (i <= j) {
mid = (i + j) / 2;
if (nums[mid] > upper) {
j = mid - 1;
} else if (mid == size - 1 || nums[mid + 1] > upper) {
upperBound = mid;
break;
} else {
i = mid + 1;
}
}
if (upperBound == -1) return 0;
i = 0;
j = size - 1;
while (i <= j) {
mid = (i + j) / 2;
if (nums[mid] < lower) {
i = mid + 1;
} else if (mid == 0 || nums[mid - 1] < lower) {
lowerBound = mid;
break;
} else {
j = mid - 1;
}
}
if (lowerBound == -1) return 0;
return upperBound - lowerBound + 1;
}
int countRangeSum(vector<int>& nums, int start, int end, int lower, int upper) {
if (start == end) {
return (nums[start] >= lower && nums[start] <= upper) ? 1 : 0;
}
int total = 0, mid = (end + start) / 2;
total += countRangeSum(nums, start, mid, lower, upper);
total += countRangeSum(nums, mid + 1, end, lower, upper);
int* first = new int[mid - start + 1];
int* second = new int[end - mid];
int i, j;
first[0] = 0;
for (i = 1, j = mid - 1; j >= 0 && j >= start; j--) {
first[i] = first[i - 1] + nums[j];
i++;
}
second[0] = nums[mid] + nums[mid + 1];
for (i = 1, j = mid + 2; j <= end; j++) {
second[i] = second[i - 1] + nums[j];
i++;
}
sort(second, second + end - mid);
for (i = 0; i < mid - start + 1; i++) {
total += binarySearch(second, end - mid, lower - first[i], upper - first[i]);
}
delete[] first;
delete[] second;
return total;
}
int countRangeSum(vector<int>& nums, int lower, int upper) {
if (nums.size() == 0) { return 0; }
return countRangeSum(nums, 0, nums.size() - 1, lower, upper);
}
};
这道题一开始就想到了这种解法,但是因为对二分查找找upper_bound,lower_bound的熟悉度的问题,耗费了一些时间。