[leetcode] Divide and Conquer

下面简单聊聊leetcode中的两道类似的DC的题目。之所以来写博客,是因为它们的思路确实与以往遇到的简单的DC有所不同。

1. 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 .

我采用的方法是经典的分治法,即把数组从中间分成两半先求两边的然后合并形成最终结果,这样递归求解。
divide即把原始问题化成了两个比较小的数组中count问题,但是这里conquer并不是特别直观。
divide的时候我们已经把A[l,m]和A[m+1,r]里面满足要求的range都计数了,所以conquer的时候要计数的是横跨这两个数组的满足要求的range,而range的起点可以是l,l+1,...,m,终点可以是m+1,m+2,...,r,最直观的方法去枚举终点和起点,那么这种算法是O(n^2)的,不满足题目要求。
这里的一个小技巧是:如果把横跨两个子数组的range从m分成两半的话,即range=range1+range2,range1一定是以m结尾,range2一定是以m+1开始。那么,我们可以先求输出所有的range1的和,然后去求range2时只需要去range1中找出符合要求的和的数目,而且预先求出来的range1可以先排好序,这样就可以采用二分查找,所以conquer的时间复杂度就是O(nlogn)。
这样总的时间复杂度就是O(n(logn)^2)。

class Solution {
public:
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        if(0 == nums.size()) return 0;
        return countRangeSum(nums, 0, nums.size()-1, lower, upper);
    }
private:
    int countRangeSum(vector<int>& nums, int l, int r, int lower, int upper) {
        if(l > r) return 0;
        if(l == r) return (lower <= nums[l] && nums[l] <= upper) ? 1 : 0;
       
        int m = l + ((r-l)>>1);
        vector<long> sums;
        sums.push_back(nums[m]);
        for(int i = m-1; i >= l; --i)
            sums.push_back(nums[i]+sums.back());
        sort(sums.begin(), sums.end());
       
        long tmp = 0, count = 0;
        for(int i = m+1; i <= r; ++i)
        {
            tmp += nums[i];
            count += (BinSearch(sums, upper-tmp+0.5) - BinSearch(sums, lower-tmp-0.5));
        }
       
        return countRangeSum(nums, l, m, lower, upper) + countRangeSum(nums, m+1, r, lower, upper) + count;
    }
   
    int BinSearch(vector<long> &sums, double target) {
        int l = 0, r = sums.size() - 1;
        while(l <= r)
        {
            int m = l + ((r-l)>>1);
            if(sums[m] > target) r = m-1;
            else l = m+1;
        }
        return r;
    }
};


2. Count number of smaller numbers of After self

You are given an integer array nums and you have to return a new counts array. The counts array has the property where counts[i] is the number of smaller elements to the right of nums[i].

Example:

Given nums = [5, 2, 6, 1]

To the right of 5 there are 2 smaller elements (2 and 1).
To the right of 2 there is only 1 smaller element (1).
To the right of 6 there is 1 smaller element (1).
To the right of 1 there is 0 smaller element.

Return the array [2, 1, 1, 0].

思路类似,这里不再赘述,直接copy代码。

class Solution {
public:
    vector<int> countSmaller(vector<int>& nums) {
        vector<int> ret;
        if(0 == nums.size()) return ret;
        ret.assign(nums.size(), 0);
        countSmaller(nums, ret, 0, nums.size()-1);
        return ret;
    }
private:
    void countSmaller(vector<int>& nums, vector<int>& ret, int l, int r) {
        if(l >= r) return;
        if(l+1 == r) {ret[l] += (int)(nums[r] < nums[l]); return;}
        
        int m = l + ((r-l)>>1);
        countSmaller(nums, ret, l, m);
        countSmaller(nums, ret, m+1, r);
        
        vector<int> tmp;
        for(int i = m+1; i <= r; ++i) tmp.push_back(nums[i]);
        sort(tmp.begin(), tmp.end());
        for(int i = l; i <= m; ++i) 
            ret[i] += lower_bound(tmp.begin(), tmp.end(), nums[i]-0.5) - tmp.begin();
    }
};







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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值