327-Count of Range Sum

Description:
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.


问题描述:
给定一个整数数组,返回在[lower,upper]范围的所有区间和的个数。
区间和S(i, j)为数组在[i,j]范围的元素之和


问题分析:
两种解法,分而治之和二分索引树.如果想完整的弄清楚这两种算法该怎么做,可以看看这个链接;
https://leetcode.com/problems/count-of-range-sum/discuss/78006/Summary-of-the-Divide-and-Conquer-based-and-Binary-Indexed-Tree-based-solutions
如果对二分索引树不熟悉的话,可以看看这个链接:
https://www.geeksforgeeks.org/binary-indexed-tree-or-fenwick-tree-2/


解法1(分而治之):

/*
思路概述如下:
令O(0, n - 1),表示原问题的解
mid = 0 + (n - 1) / 2,那么原问题可以分解为:
O(0, n - 1) = O(0, mid) + O(mid + 1, n - 1) + C
其中C表示i位于[0, mid],j位于[mid + 1, n - 1]的解
那么对于左半边可以维护一个后缀数组(与前缀数组的顺序相反),右半边可以维护一个前缀数组,将
前缀数组排序后,通过二分查找确定边界范围
*/
class Solution {
    public int countRangeSum(int[] nums, int lower, int upper) {
        if(nums == null || nums.length == 0 || lower > upper)   return 0;
        return mergeSort(nums, 0, nums.length - 1, lower, upper);
    }
    public int mergeSort(int[] nums, int l, int r, int lower, int upper){
        if(l == r)   return lower <= nums[l] && nums[l] <= upper ? 1 : 0;

        int m = l + (r - l) / 2;
        //前缀数组
        long[] arr = new long[r - m];

        long sum = 0;
        for(int i = m + 1;i <= r;i++){
            sum += nums[i];
            arr[i - m - 1] = sum;
        }
        //对前缀数组排序
        Arrays.sort(arr);

        sum = 0;
        int count = 0;
        for(int i = m;i >= l;i--){
            sum += nums[i];
            //通过二分查找确定范围
            count += findIndex(arr, upper - sum + 0.5) - findIndex(arr, lower - sum - 0.5);            
        }
        //将原问题分解为子问题,分而治之
        return mergeSort(nums, l, m, lower, upper) +  mergeSort(nums, m + 1, r, lower, upper) + count;
    }
    public int findIndex(long[] arr, double val){
        int left = 0, right = arr.length - 1, mid = -1;

        while(left <= right){
            mid = left + (right - left) / 2;
            if(arr[mid] > val){
                right = mid - 1;
            }else{
                left = mid + 1;
            }
        }

        return left;
    }
}

解法2(归并排序):

class Solution {
    public int countRangeSum(int[] nums, int lower, int upper) {
        int n = nums.length;
        long[] sums = new long[n + 1];
        for (int i = 0; i < n; ++i)
            sums[i + 1] = sums[i] + nums[i];
        return countWhileMergeSort(sums, 0, n + 1, lower, upper);
    }

    private int countWhileMergeSort(long[] sums, int start, int end, int lower, int upper) {
        if (end - start <= 1) return 0;
        int mid = (start + end) / 2;
        int count = countWhileMergeSort(sums, start, mid, lower, upper) 
                  + countWhileMergeSort(sums, mid, end, lower, upper);
        int j = mid, k = mid, t = mid;
        long[] cache = new long[end - start];
        for (int i = start, r = 0; i < mid; ++i, ++r) {
            while (k < end && sums[k] - sums[i] < lower) k++;
            while (j < end && sums[j] - sums[i] <= upper) j++;
            while (t < end && sums[t] < sums[i]) cache[r++] = sums[t++];
            cache[r] = sums[i];
            count += j - k;
        }
        System.arraycopy(cache, 0, sums, start, t - start);
        return count;
    }
}

解法3(二分索引树):

class Solution {
    public int countRangeSum(int[] nums, int lower, int upper) {
        long[] sum = new long[nums.length + 1];
        long[] cand = new long[3 * sum.length + 1];
        int index = 0;
        cand[index++] = sum[0];
        cand[index++] = lower + sum[0] - 1;
        cand[index++] = upper + sum[0];

        for (int i = 1; i < sum.length; i++) {
            sum[i] = sum[i - 1] + nums[i - 1];
            cand[index++] = sum[i];
            cand[index++] = lower + sum[i] - 1;
            cand[index++] = upper + sum[i];
        }

        cand[index] = Long.MIN_VALUE; // avoid getting root of the binary indexed tree when doing binary search
        Arrays.sort(cand);

        int[] bit = new int[cand.length];

        // build up the binary indexed tree with only elements from the prefix array "sum"
        for (int i = 0; i < sum.length; i++) {
            addValue(bit, Arrays.binarySearch(cand, sum[i]), 1);
        }

        int count = 0;

        for (int i = 0; i < sum.length; i++) {
            // get rid of visited elements by adding -1 to the corresponding tree nodes
            addValue(bit, Arrays.binarySearch(cand, sum[i]), -1);

            // add the total number of valid elements with upper bound (upper + sum[i])
            count += query(bit, Arrays.binarySearch(cand, upper + sum[i]));

            // minus the total number of valid elements with lower bound (lower + sum[i] - 1)
            count -= query(bit, Arrays.binarySearch(cand, lower + sum[i] - 1));
        }

        return count;
    }

    private void addValue(int[] bit, int index, int value) {
        while (index < bit.length) {
            bit[index] += value;
            index += index & -index;
        }
    }

    private int query(int[] bit, int index) {
        int sum = 0;

        while (index > 0) {
            sum += bit[index];
            index -= index & -index;
        }

        return sum;
    }
}

你可能感兴趣的:(算法,数据结构,leetcode,算法与数据结构,leetcode全解)