327.区间的个数
前缀数组和treemap的应用
题干:
给定一个整数数组 nums,返回区间和在 [lower, upper] 之间的个数,包含 lower 和 upper。
区间和 S(i, j) 表示在 nums 中,位置从 i 到 j 的元素之和,包含 i 和 j (i ≤ j)。
说明:
最直观的算法复杂度是 O(n2) ,请在此基础上优化你的算法。
解释:
非常直白,限定高低,返回区间和在这个范围内的区间个数
思考:
先不考虑时间复杂度的事情,这个问题实际上是让我们统计区间和的频数。 回顾一下计算区间和,先从第一位开始累加,得到所有位置的累加和,然后两两相减,这样就得到了所有的区间和。最普通的办法,时间复杂度 O(n2) 那么回到复杂度要求,O(n)是不可能的,那就只能O(nLogn),所以肯定是要二分查找。 获取累加和那一步是不能动的,所以就是对每个位置,之前是要遍历(0,i-1)的所有位置,这里实际上有重复,注意这里遍历的时候是可以无序的,所以对于每一个位置i,只要在前面所有的位置里二分查找上下限即可。 这样又会带来一个新的问题,如何在插入之后自动保持有序?可以考虑使用一个堆排序,每次插入耗时O(1),每次调整耗时(logn),两次查找也是logn,这样就只需要nlogn的复杂度了。
答案:
class Solution {
public int countRangeSum(int[] nums, int lower, int upper) {
if(nums == null || nums.length == 0){
return 0;
}
//键值为区间和和这个区间和出现的次数
TreeMap<Long, Integer> tree = new TreeMap<>();
tree.put(0L, 1);
int count = 0;
long sum = 0L;
for(int num : nums){
sum += num;
//subMap()返回一个值在sum - upper 和sum - lower 之间的子集合,values()方法获得这个映射的值得视图
for(int cnt : tree.subMap(sum - upper, true, sum - lower, true).values()){
count += cnt; //统计满足条件的区间和个数
}
tree.put(sum, tree.getOrDefault(sum, 0) + 1);
}
return count;
}
}
答案补充:
这个答案算是练习一下treemap的使用,首先注意treemap是去重的,所以要统计频率 还要注意subMap这个方法默认是左闭右开的,最后就是前缀数组是整形叠加,一定要注意用long。