每天两道LeetCodeHard:(51)

175 阅读2分钟

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。