leetcode 327 特殊分治 双指针 线段树

18 篇文章 0 订阅
8 篇文章 0 订阅

在这里插入图片描述

解法一:分治 O(NlogN)
这个题用分治做其实很难想。对于求区间和问题,前缀数组很有用。但这个题要求所有区间范围,如果枚举需要 O ( N 2 ) O(N^2) O(N2),其实也可以接受。
用分治的话,其实就是想明白了一件事,只要确保i < j,i,j在哪个位置其实是不影响结果的,因为我们只需要求区间个数。这就很好办了,分治就是划分,然后处理两个有序数组。利用分治可以很容易得到所有上述数组,用O(N)时间计算区间个数+合并即可。
在计算区间个数时,可以用到双指针。对第二个有序数组,咱们可以用双指针确定它的最小界和最大界。对第一个数组的每个元素都计算这样的界限即可。由于有序的特性,指针只能向右移动,故很有效。
也可以用二分来查找上界和下界,但对于有序多次查找,双指针更快。

typedef long long ll;
class Solution {
ll lower, upper;

public:
    void msort(vector<ll>& v, int& sum, int l, int r)
    {
        if(l == r)
        {
            if(v[l] >= lower && v[l] <= upper)sum += 1;
            return;
        }

        int mid = l + (r-l)/2;
        msort(v, sum, l, mid);
        msort(v, sum, mid+1, r);

        // merge
        int p1 = mid+1, p2 = mid+1;
        for(int p0=l;p0<=mid;p0++)
        {
            while(p1 <= r && v[p1]-v[p0] < lower)p1++;
            while(p2 <= r && v[p2]-v[p0] <= upper)p2++;
            sum += p2-p1;
        }
        inplace_merge(v.begin()+l, v.begin()+mid+1, v.begin()+r+1);
    }

    int countRangeSum(vector<int>& nums, int lower, int upper) {
        if(nums.size() == 0)return 0;
 
        this->lower = lower;
        this->upper = upper;
        int sum = 0;
        vector<ll> pref{nums[0]};
        for(int i=1;i<nums.size();i++)
            pref.push_back(nums[i]+pref[i-1]);
        msort(pref, sum, 0, pref.size()-1);

        return sum;
    }
};

解法二:平衡二叉树
在解法一中,在得到前缀数组后,我们对于任何一个数,希望找到他的上界和下界,从而求出区间个数。解法一通过归并排序实现了查找。但平衡二叉树其实就是为这个问题而设计的。利用multiset,我们很容易用少量代码解决这个问题。
还是要利用解法一中分析的分割问题,这次,我们只需一个一个考虑。对每一个新元素,考虑已放入数组的上界和下界,相减即可。
这个算法很慢是因为muliset的迭代器不支持随机访问,相减是O(N)的,导致这个算法又变成平方复杂度。所以,用平衡树实际是不可行的。超时不是因为迭代器出bug,是因为真的超时了。一定要用的话可以用二分找到位置,相减即可。

typedef long long ll;
class Solution {
public:
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        if(nums.size() == 0)return 0;
        vector<ll> pref{nums[0]};
        for(int i=1;i<nums.size();i++)
            pref.push_back(nums[i]+pref[i-1]);

        // 查找
        int ret = 0;
        multiset<ll> s;
        for(auto i: pref)
        {
            if(i >= lower && i <= upper)ret += 1;
            ll s0 = i-upper, s1 = i-lower;
            auto it1 = s.lower_bound(s0), it2 = s.upper_bound(s1);
            ret += distance(it1, it2);
            s.insert(i);
        }

        return ret;
    }
};

解法后续:线段树、树状数组
后面补上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值