LeetCode_327 Count of Range Sum

来源:互联网 发布:淘宝赤兔插件备注设置 编辑:程序博客网 时间:2024/06/08 17:35

LeetCode_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.

Credits:
Special thanks to @dietpepsi for adding this problem and creating all test cases.

Link:
https://leetcode.com/problems/count-of-range-sum/description/

解决方案

本题是求数组的区间和在给定范围内的个数。数组的区间和s(i,j)指的是索引在ij(i j)之间的所有元素的和,给定范围在[lower,upper]之间,求出所有满足lower s(i,j) upper的区间[i,j]的个数。

由之前类似的题目Range Sum Query - Immutable,Range Sum Query 2D - Immutable 和 Range Sum Query - Mutable,求区间和的问题一般是先计算累加和sums[i]=nums[0]+nums[1]+ ··· +nums[i],则s(i,j)=sums[j]-sums[i]

通过参考资料,解决该题的基本思想是:

  • 对累加和数组的某个元素sums[i],对其之后(或之前)的元素进行排序。
  • 对于有序序列sorted[start,end],找出满足sorted[j] lower的第一个sorted[j]和满足sorted[k] > upper的第一个sorted[k],则k-j即为数组中在[lower,upper]范围内的所有元素的个数。

下面是两种解法,其中解法一被使用和引用的最多。

解法一

采用“归并排序(Merge Sort)”来求解,基本思路是将累加和数组sums[start,end]分为左右两半,其中左半部[start,mid)和右半部[mid,end)先分别递归调用解法一求出各自的满足条件的区间和个数,并完成各自部分的排序。然后再将已经排好序的左右两部进行归并,完成整体排序。在整体排序过程中,遍历左半部分的sums[i],找出右半部分的sums[j]sums[k],使其满足:

  • sums[j]是第一个满足 sums[j] sums[i] lower 的元素。
  • sums[k]是第一个满足 sums[k] sums[i] > upper 的元素。

k-j便是在[lower,upper]之间的区间和的个数。因为采用自底向上的归并排序,所以保证了对每一个sums[i],能够找到其后所有满足条件的sums[j]sums[k]

在整体排序的过程中,还需要一个缓存数组cache来存储每次遍历i时的左右半排序结果,通过复制完成sums[start,end]间的整体排序。

以下为源码:

//C++ Merge Sortclass Solution {public:    int countRangeSum(vector<int>& nums, int lower, int upper) {        int len=nums.size();        vector<long long> sums(len+1,0);        for(int i=0;i<len;i++){            sums[i+1]=sums[i]+nums[i];        }        return countAndMergeSort(sums,0,len+1,lower,upper);            }    int countAndMergeSort(vector<long long>& sums,int start,int end,int lower,int upper){        if(end-start<=1)return 0;        vector<long long> cache(end-start);        int middle=(start+end)/2;        int cnt=countAndMergeSort(sums,start,middle,lower,upper)+            countAndMergeSort(sums,middle,end,lower,upper);        int j=middle,k=middle,t=middle;        int r=0;        for(int i=start;i<middle;++i){            while(j<end && sums[j]-sums[i]<lower)j++;            while(k<end && sums[k]-sums[i]<=upper)k++;            while(t<end && sums[t]<sums[i])cache[r++]=sums[t++];            cache[r++]=sums[i];            cnt+=k-j;        }        copy(cache.begin(),cache.begin()+r,sums.begin()+start);        return cnt;    }};

解法二

使用multiset,遍历nums[i],逐一将i位置的累加和插入到multiset中,保证已经插入的累加和有序。对于位置i,multiset中已经有按序排列的前i个累加和。目标是找到i前的满足条件lower sums[i] sums[j] uppersums[j]的个数,即寻找满足条件 sums[i] upper sums[j] sums[i] lower 的两个临界值之间的元素个数。临界值可以使用lower_bound(返回有序集合中大于等于给定值的第一个数)和upper_bound(返回有序集合中大于给定值的第一个数)来求得。

以下为源码:

//C++ multisetclass Solution {public:    int countRangeSum(vector<int>& nums, int lower, int upper) {        multiset<long long> sums;        sums.insert(0);        int res=0;        long long sum=0;        for(int i=0;i<nums.size();i++){            sum+=nums[i];            res+=distance(sums.lower_bound(sum-upper),sums.upper_bound(sum-lower));            sums.insert(sum);        }        return res;    }};

相关题目

LeetCode_303 Range Sum Query - Immutable
LeetCode_304 Range Sum Query 2D - Immutable
LeetCode_307 Range Sum Query - Mutable

参考资料

https://leetcode.com/problems/count-of-range-sum/discuss/
https://www.cnblogs.com/grandyang/p/5162678.html


原创粉丝点击