Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LeetCode] 327. Count of Range Sum #327

Open
grandyang opened this issue May 30, 2019 · 2 comments
Open

[LeetCode] 327. Count of Range Sum #327

grandyang opened this issue May 30, 2019 · 2 comments

Comments

@grandyang
Copy link
Owner

grandyang commented May 30, 2019

 

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 ( n 2) is trivial. You MUST do better than that.

Example:

Input: _nums_ = [-2,5,-1], _lower_ = -2, _upper_ = 2,
Output: 3 
Explanation: 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.

 

这道题给了我们一个数组,又给了一个下限和一个上限,让求有多少个不同的区间使得每个区间的和在给定的上下限之间。这道题的难度系数给的是 Hard,的确是一道难度不小的题,题目中也说了 Brute Force 的方法太 Naive 了,只能另想方法了。To be honest,这题超出了博主的能力范围,所以博主也没挣扎了,直接上网搜大神们的解法啦。首先根据前面的那几道类似题 Range Sum Query - MutableRange Sum Query 2D - Immutable 和 Range Sum Query - Immutable 的解法可知类似的区间和的问题一定是要计算累积和数组 sums 的,其中 sum[i] = nums[0] + nums[1] + ... + nums[i],对于某个i来说,只有那些满足 lower <= sum[i] - sum[j] <= upper 的j能形成一个区间 [j, i] 满足题意,目标就是来找到有多少个这样的 j (0 =< j < i) 满足 sum[i] - upper =< sum[j] <= sum[i] - lower,可以用 C++ 中由红黑树实现的 multiset 数据结构可以对其中数据排序,然后用 upperbound 和 lowerbound 来找临界值。lower_bound 是找数组中第一个不小于给定值的数(包括等于情况),而 upper_bound 是找数组中第一个大于给定值的数,那么两者相减,就是j的个数,参见代码如下:

 

解法一:

class Solution {
public:
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        int res = 0;
        long long sum = 0;
        multiset<long long> sums;
        sums.insert(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;
    }        
};

 

我们再来看一种方法,这种方法的思路和前一种一样,只是没有 STL 的 multiset 和 lower_bound 和 upper_bound 函数,而是使用了 Merge Sort 来解,在混合的过程中,已经给左半边 [start, mid) 和右半边 [mid, end) 排序了。当遍历左半边,对于每个i,需要在右半边找出k和j,使其满足:

j是第一个满足 sums[j] - sums[i] > upper 的下标

k是第一个满足 sums[k] - sums[i] >= lower 的下标 

那么在 [lower, upper] 之间的区间的个数是 j - k,同时也需要另一个下标t,用来拷贝所有满足 sums[t] < sums[i] 到一个寄存器 Cache 中以完成混合排序的过程,这个步骤是混合排序的精髓所在,实际上这个寄存器的作用就是将 [start, end) 范围内的数字排好序先存到寄存器中,然后再覆盖原数组对应的位置即可,(注意这里 sums 可能会整型溢出,使用长整型 long 代替),参见代码如下:

 

解法二:

class Solution {
public:
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        vector<long> sums(nums.size() + 1, 0);
        for (int i = 0; i < nums.size(); ++i) {
            sums[i + 1] = sums[i] + nums[i];
        }
        return countAndMergeSort(sums, 0, sums.size(), lower, upper);
    }
    int countAndMergeSort(vector<long>& sums, int start, int end, int lower, int upper) {
        if (end - start <= 1) return 0;
        int mid = start + (end - start) / 2;
        int cnt = countAndMergeSort(sums, start, mid, lower, upper) + countAndMergeSort(sums, mid, end, lower, upper);
        int j = mid, k = mid, t = mid;
        vector<int> cache(end - start, 0);
        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];
            cnt += j - k;
        }
        copy(cache.begin(), cache.begin() + t - start, sums.begin() + start);
        return cnt;
    }
};

 

Github 同步地址:

#327

 

类似题目:

Range Sum Query - Mutable 

Range Sum Query 2D - Immutable

Range Sum Query - Immutable

Reverse Pairs

Count of Smaller Numbers After Self

 

参考资料:

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

https://leetcode.com/problems/count-of-range-sum/discuss/77990/Share-my-solution

https://leetcode.com/problems/count-of-range-sum/discuss/78006/Summary-of-the-Divide-and-Conquer-based-and-Binary-Indexed-Tree-based-solutions

https://leetcode.com/problems/count-of-range-sum/discuss/78030/8-line-multiset-C%2B%2B-solution-(100ms)-also-binary-search-tree-(180ms)-%2B-mergesort(52ms)

 

LeetCode All in One 题目讲解汇总(持续更新中...)

@lei-hsia
Copy link

您好, 请问"需要另一个下标t,用来拷贝所有满足 sums[t] < sums[i] 到一个寄存器 Cache 中以完成混合排序的过程", 这句话是什么意思啊? 我不是很懂t和`cache1在这里的作用

@grandyang
Copy link
Owner Author

这个步骤是混合排序的精髓所在,实际上这个寄存器的作用就是将 [start, end) 范围内的数字排好序先存到寄存器中,然后再覆盖原数组对应的位置

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants