[leetCode]327. 區間和的個數

wuzheng228發表於2020-11-29

題目

連結:https://leetcode-cn.com/problems/count-of-range-sum

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

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

示例:

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

歸併排序

由於要求區間和因此可以想到字首和陣列,設字首和陣列為preSum
問題可以轉換為 preSum[j] - preSum[i] 屬於[lower, upper]
如果給定兩個升序排列的陣列n1,n2, 嘗試找出下標對(i, j), 滿足: n2[j] - n1[i] 屬於[lower, upper]
由於兩個陣列是升序排列的所以只需要在n2中維護兩個指標 l, r, 一開始都指向n2的起始位置,首先考慮n1的第一個元素,l向有移動知道找到一個元素使n2[l] - n1[0] >= lower為止,這樣l右側的元素均大於n1[0] + lower, 再移動r指標,直到n2[r] - n1[0] > upper這樣r左側的元素均小於等於n1[0] + upper,所以區間[l, r)的所有下標j都滿足n2[j] - n1[i] 屬於[lower, upper] 。下面繼續考察n1的第二個元素,由於n1是遞增的所以l, r繼續向右移動。 在上述過程中對於n1的每一個下標都應記錄相應區間[l, r)的大小,最終就統計得到了下標對(i, j)的數量。
在這裡插入圖片描述

class Solution {
    private int lower;

    private int upper;

    public int countRangeSum(int[] nums, int lower, int upper) {
        this.lower = lower;
        this.upper = upper;
        int n = nums.length;
        long[] preSum = new long[n + 1];
        for (int i = 0; i < n; i++) {
            preSum[i + 1] = preSum[i] + nums[i];
        }
        return mergeSort(preSum, 0, preSum.length - 1);
    }

    public int mergeSort(long[] sums, int lo, int hi) {
        if (lo >= hi) {
            return 0;
        }
        int mid = lo + (hi - lo) / 2;
        int ans = 0;
        ans += mergeSort(sums, lo, mid);
        ans += mergeSort(sums, mid +  1, hi);
        ans += merge(sums, lo, mid, hi);
        return ans;
    }

    public int merge(long[] sums, int lo, int mid, int hi) {
        int i = lo, l = mid + 1, r = mid + 1;
        int count = 0;
        while (i <= mid) {
            while (l <= hi && sums[i] + lower > sums[l]) { // l 右側的元素都大於等於 sums[i] + lower
                l++;
            }
            while (r <= hi && sums[i] + upper >= sums[r]) { // r 左側的元素都小於等於 sums[i] + upper
                r++;
            }
            count+= r - l;
            i++;
        }

        int[] aux = new int[hi - lo + 1]; 
        int idx = 0;
        int p1 = lo, p2 = mid + 1;
        while (p1 <= mid && p2 <= hi) {
            if (sums[p1] < sums[p2]) {
                aux[idx++] = (int) sums[p1++];
            } else {
                aux[idx++] = (int) sums[p2++];
            }
        }

        while (p1 <= mid) {
            aux[idx++] = (int) sums[p1++];
        }
        while (p2 <= hi) {
            aux[idx++] = (int) sums[p2++];
        }

        for (int j = lo; j <= hi; j++) {
            sums[j] = aux[j-lo];
        }

        return count;
    }
}

相關文章