【Leetcode】327. Count of Range Sum 327. 区间和的个数

1

解法

naive的方法是 O ( n 2 ) O(n^2) O(n2)
要找更高效的方法,都离不开先计算出一个前缀和数组p,然后对于每个元素p[i],要计算i以后有多少个元素在[p[i]+lower,p[i]+upper]范围内

解法一:归并排序

在merge的过程中会得到两个有序的数组LR
对于L里的每个元素a,需要在R里找到[a+lower,a+upper]范围里的元素的个数,由于R是有序的,所以用尺取法就可以在 O ( n ) O(n) O(n)时间内完成,所以最后复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

class Solution(object):
    def countRangeSum(self, nums, lower, upper):
        """
        :type nums: List[int]
        :type lower: int
        :type upper: int
        :rtype: int
        """
        from bisect import bisect_left,bisect_right,insort
        if not nums or upper<lower:
            return 0
        p = [0]
        for a in nums:
            p.append(p[-1]+a)
        n = len(p)
        self.ans = 0
        def mergesort(A):
            if len(A)<=1:
                return A
            mid = len(A)>>1
            B,C = mergesort(A[:mid]),mergesort(A[mid:])
            n = len(C)
            l = r = 0
            for b in B:
                lo,hi = b+lower,b+upper
                while l<n and C[l]<lo:
                    l += 1
                while r<n and C[r]<=hi:
                    r += 1
                self.ans += r-l
            res = []
            i = j = 0
            while i<mid:
                while j<n and C[j]<B[i]:
                    res.append(C[j])
                    j += 1
                res.append(B[i])
                i += 1
            while j<n:
                res.append(C[j])
                j += 1
            return res
        mergesort(p)
        return self.ans

解法二:TreeMap

倒序遍历前缀和数组,当遍历到p[i]的时候,它往后的元素都已经遍历过,它们都插入了一个有序的TreeMap,查找两个边界需要 O ( l o g n ) O(logn) O(logn),然后再插入p[i]需要 O ( l o g n ) O(logn) O(logn),问复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
由于python没有TreeMap的结构,所以用bisect来代替

class Solution(object):
    def countRangeSum(self, nums, lower, upper):
        """
        :type nums: List[int]
        :type lower: int
        :type upper: int
        :rtype: int
        """
        from bisect import bisect_left,bisect_right,insort
        if not nums or upper<lower:
            return 0
        p = [0]
        for a in nums:
            p.append(p[-1]+a)
        walked = []
        ans = 0
        for a in p[::-1]:
            l,r = a+lower,a+upper
            i,j = bisect_left(walked,l),bisect_right(walked,r)
            ans += j-i
            insort(walked,a)
            # print walked
        return ans

解法三:离散化树状数组

这种统计在某个区间内的数字的个数,也可以用树状数组来解决
由于数字的范围可能很大,所以要离散化,涉及到的需要被离散化的值有p[i],p[i]+lower,p[i]+upper
树状数组的查询是 O ( l o g n ) O(logn) O(logn)的,所以最后复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

class Solution(object):
    def countRangeSum(self, nums, lower, upper):
        """
        :type nums: List[int]
        :type lower: int
        :type upper: int
        :rtype: int
        """
        if not nums or upper<lower:
            return 0
        p = [0]
        for a in nums:
            p.append(p[-1]+a)
        value = set()
        for a in p:
            value |= {a,a+lower,a+upper}
        value = sorted(value)
        idx={}
        for i,a in enumerate(value):
            idx[a]=i
        n = len(value)
        c = [0]*n
        def lowbit(k):
            return k&-k
        def add(k):
            k +=1
            while k<=n:
                c[k-1]+=1
                k+=lowbit(k)
        def tsum(k):
            res = 0
            k+=1
            while k>=1:
                res += c[k-1]
                k -= lowbit(k)
            return res
        ans = 0
        for a in p[::-1]:
            l,r = idx[a+lower],idx[a+upper]
            ans += tsum(r)
            if l>0:
                ans -= tsum(l-1)
            add(idx[a])
        return ans

emmm……最后最快的是方法二

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值