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.

分析:用分治算法,对于 (a0,a1,a2,...,an1) ,所有可能的解区间是 (a0,a1,a2,...,a(n1)/2) (a(n1)/2+1,a(n1)/2+2,a(n1)/2+3,...,an1) 中的解区间以及横跨两部分的解区间。
我们要需要考虑的是如何求解横跨两部分的解区间。因为我们要做到优于 O(n2) 的算法,在递归式中 T(n)=2T(n/2)+f(x) f(x) 至少应该是 O(nlgn) ,这样才能得到 O(nlgn) 的算法。

举个栗子,nums = [-2, 5, -1,7,-4,9],横跨两部分的区间一定至少包括[-1,7],然后向两边进行扩展。如果对于左边n/2-1个数,尝试着与右边的每个数进行匹配,那么 f(x) 会达到 O(n2) 。这里的做法是将右边每一个数到-1的区间和存在另一个数组中,对数组进行 O(nlgn) 的排序,然后对左边n/2-1个数分别进行 O(lgn) 的二分查找。这样使得 f(x) O(nlgn) ,根据Master Theory,整个分治算法的复杂度为 O(nlgn)

#include <vector>
#include <algorithm>
#include <iostream>

using std::sort;
using std::cout;
using std::endl;
using std::vector;

class Solution {
public:
    int binarySearch(int* nums, int size, int lower, int upper) {
        int i = 0, j = size - 1, mid;
        int upperBound = -1, lowerBound = -1;
        while (i <= j) {
            mid = (i + j) / 2;
            if (nums[mid] > upper) {
                j = mid - 1;
            } else if (mid == size - 1 || nums[mid + 1] > upper) {
                upperBound = mid;
                break;
            } else {
                i = mid + 1;
            }
        }
        if (upperBound == -1) return 0;
        i = 0;
        j = size - 1;
        while (i <= j) {
            mid = (i + j) / 2;
            if (nums[mid] < lower) {
                i = mid + 1;
            } else if (mid == 0 || nums[mid - 1] < lower) {
                lowerBound = mid;
                break;
            } else {
                j = mid - 1;
            }
        }
        if (lowerBound == -1) return 0;
        return upperBound - lowerBound + 1;
    }
    int countRangeSum(vector<int>& nums, int start, int end, int lower, int upper) {
        if (start == end) {
            return (nums[start] >= lower && nums[start] <= upper) ? 1 : 0;
        }
        int total = 0, mid = (end + start) / 2;
        total += countRangeSum(nums, start, mid, lower, upper);
        total += countRangeSum(nums, mid + 1, end, lower, upper);
        int* first = new int[mid - start + 1];
        int* second = new int[end - mid];
        int i, j;
        first[0] = 0;
        for (i = 1, j = mid - 1; j >= 0 && j >= start; j--) {
            first[i] = first[i - 1] + nums[j];
            i++;
        }
        second[0] = nums[mid] + nums[mid + 1];
        for (i = 1, j = mid + 2; j <= end; j++) {
            second[i] = second[i - 1] + nums[j];
            i++;
        }
        sort(second, second + end - mid);
        for (i = 0; i < mid - start + 1; i++) {
            total += binarySearch(second, end - mid, lower - first[i], upper - first[i]);
        }
        delete[] first;
        delete[] second;
        return total;
    }
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        if (nums.size() == 0) { return 0; }
        return countRangeSum(nums, 0, nums.size() - 1, lower, upper);
    }
};

这道题一开始就想到了这种解法,但是因为对二分查找找upper_bound,lower_bound的熟悉度的问题,耗费了一些时间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值