1. 程式人生 > 實用技巧 >327. 區間和的個數

327. 區間和的個數

給定一個整數陣列nums,返回區間和在[lower, upper]之間的個數,包含lower和upper。
區間和S(i, j)表示在nums中,位置從i到j的元素之和,包含i和j(i ≤ j)。

說明:
最直觀的演算法複雜度是O(n2) ,請在此基礎上優化你的演算法。

示例:

輸入: nums = [-2,5,-1], lower = -2, upper = 2,
輸出: 3
解釋: 3個區間分別是: [0,0], [2,2], [0,2],它們表示的和分別為: -2, -1, 2。

class Solution {
public:
    //字首和 sum[i...j]=pre[j]-pre[i] c++ set是有序的
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        vector<long> preSum;
        long sum = 0;
        for(int i=0;i<nums.size();i++){
            sum += nums[i];
            preSum.push_back(sum);
        }
        int res=0;
        multiset<long> pre;
        pre.insert(0);
        for(int i=0;i<preSum.size();i++){
            // if(preSum[i]>= lower &&  preSum[i]<=upper){
            //         res++;
            // }
            // for(int j=i+1;j<preSum.size();j++){
            //     //這裡找preSum[i]的過程導致兩層迴圈。
            //     //lower <= preSum[j]-preSum[i] <= upper
            //     //              preSum[j]-upper =< preSum[i] <= preSum[j]-lower
            //     //查詢: preSum[i] 大於某個數,小於某個數的個數,若是有序陣列,可以使用二分查詢。
            //     //
            //     // if(preSum[j]-preSum[i]>=lower && preSum[j]-preSum[i]<=upper ){
            //     //     res++;
            //     // }
            // }
            //遇到的每一個i,都找他之前的(0-i-1)個數中 位於(當前數-upper)=< preSum[i]<=(當前-lower) 之間得數的個數
            //若是之前的0-i-1個數是有序的
            //那麼如果S陣列是  有序的  我們就可以通過兩次二分查詢計算出有多少個x滿足條件(d2-d1):
            //第一次二分查詢找出第一個大於等於 presum - upper 的位置d1;
            //第二次二分查詢找出第一個大於 presum - lower 的位置d2。
            //如何快速保證每次(0-i-1)的字首和陣列都有序?將前(0-i-1)位置的數都存入multiset,利用紅黑樹
            auto low = pre.lower_bound(preSum[i]-upper);
            auto high = pre.upper_bound(preSum[i]-lower);
            res += distance(low,high);
            pre.insert(preSum[i]);
        }
        return res;
    }
};