问题描述

给定一个整数数组nums,返回其所有落在[low, upper]范围内(包含边界)的区间和的数目。
区间和sums(i, j)的定义为所有下标为i到j之间(i ≤ j)的元素的和,包含边界。

思路:

归并排序

问题可描述为,对每个sums[i],满足lower<=sums[i] - sums[j]<=upper的j的个数,并进行求和。

一个很巧妙的方法是,对sums进行排序得到ocums,然后求ocums[m] - ocums[n] = sums[i] - sums[j]落在上下界的个数,这里想强调的是ocums[m],ocums[n]分别对应排序前的sums[i],sums[j]。 这样做的好处是排序后求ocums区间内元素个数要方便很多,比如说对ocums[m],只需找到刚好满足上界的ocums[y],和刚好满足下界的ocums[x],x-y即为个数(注意ocums是升序的,所以x一定大于y),找到上下界的方法如果不用循环线性查找(就是从小到大一个个试),就可以用算法复杂度更低的二分查找,这种思路用于归并排序。

归并排序的思路是,把区间划分为上下两个半空间x和y,然后对两个空间的数一个一个取x[i]和y[j],比较这两个数,如果x[i]<y[j]就让i+1,否则j+1,并把较小的数加入到新序列中,这样实现排序。当前排序的空间是从小到大的,也就是说需要先把区间划分到最小,然后慢慢合并。

用归并排序的思路求所有的区间和,就是先把区间sums划分到最小,然后求满足区间和上下界的个数,然后对区间进行排序,然后合并两个小区间,求满足区间和上下界的个数,然后对合并后区间进行排序,然后又合并排序后的两个区间。。。循环下去;从一棵树的角度来说,就是从根节点开始划分区间,从最底层的区间开始计算个数并排序,并向上排序,因此这是一个递归的过程,求个数的过程是在合并的过程中计算的。

# 归并排序
# 让人不解的两个点
# 1. 为什么最小只划分区间到两个数sums[i, i+1],而不是一个数?
# 这是因为操作的对象是和,同时要遵循下半区间减上半区间
# 当只有两个数的时候,实质是看nums[i] = sums[i + 1] - sums[i]是否在上下界内
# 最小区间划分不会重叠,也就不会出现sums[i - 1, i],那还有一个数nums[i - 1]呢
# 这个数会在相邻两个最小区间合并的过程中被考虑(想象一棵二分树)
# 2. 对sums进行排序后得到ocums,选择区间和ocums[i] - ocums[j]在上下界的,这个合理吗
# 问题也说了,区间和就等于sums[m] - sums[n],本质上还是求源序列的区间和在[m, n]上是否满足上下界
# 这个方法真的很妙啊
# 最后要说这个的算法复杂度是N方的,真不知道怎么就通过了
class Solution(object):
    def countRangeSum(self, nums, lower, upper):
        """
        :type nums: List[int]
        :type lower: int
        :type upper: int
        :rtype: int
        """
        size = len(nums)
        sums = [0] * (size + 1)
        for x in range(1, size + 1):
            sums[x] += nums[x - 1] + sums[x - 1]
        INF = max(sums)

        def mergeSort(lo, hi):
            if lo == hi: return 0
            mi = int((lo + hi) / 2)
            cnt = mergeSort(lo, mi) + mergeSort(mi + 1, hi)
            x = y = lo
            for i in range(mi + 1, hi + 1):
                while x <= mi and sums[i] - sums[x] >= lower:
                    x += 1
                while y <= mi and sums[i] - sums[y] > upper:
                    y += 1
                cnt += x - y
            part = sums[lo : hi + 1]

            l, h = lo, mi + 1
            for i in range(lo, hi + 1):
                x = part[l - lo] if l <= mi else INF
                y = part[h - lo] if h <= hi else INF
                if x < y: l += 1
                else: h += 1
                sums[i] = min(x, y)
            return cnt

        return mergeSort(0, size)

nums = [-2, 5, 1, 3, 1, -4, 2]
lower = -2
upper = 2
X =Solution()
print(X.countRangeSum(nums, lower, upper))

树状数组

还可以对问题就行转换,即求满足sums[i] - upper<=sums[j]<=sums[i] - lower的j的个数,最朴素的方法就是对每一个sums[i]遍历所有的(j,j<i)

# leetcode 327:求所有区间和在[lower, upper]之间的个数
# 解法1 kk...复杂度为N方,超时了
class Solution(object):
    def countRangeSum(self, nums, lower, upper):
        """
        :type nums: List[int]
        :type lower: int
        :type upper: int
        :rtype: int
        """
        ans = 0
        sum = [0 for _ in nums]
        for i, num in enumerate(nums):
            if i == 0:
                sum[i] = num
            else:
                sum[i] = sum[i - 1] + num
            if sum[i] >= lower and sum[i] <= upper:
                ans += 1
            for j in range(i):
                if sum[j] >= (sum[i] - upper) and sum[j] <= (sum[i] - lower):
                    ans += 1
        return ans

nums = [-2, 5, 1, 3, 1, -4, 2]
lower = -2
upper = 2
X =Solution()
print(X.countRangeSum(nums, lower, upper))

但用更直白的描述就是,求位于(sums[i] - upper, sums[i] - lower)的sums[j]的个数,希望通过排序知道每个值取了多少次,都排在哪个位置,当然前提是每次排序都只有序列的前i个数。也就是说,对sums[i]求个数的时候,只需把sums[i]的值插入到前面已经排好序的新序列中(这个可以用二分法做)。由于我们的目的是对个数求和,可以让序列的值为个数,然后序列的索引为sums[i],这样,树状数组的结构就出来了。

# 基于上一思路,利用树状数组来降低复杂度
# 同样是为了求落在区间[sumi - upper, sumi - lower]的sumj的个数,
# 前一朴素法是对每个sumi,直接for j in range(0, i),致使算法复杂度为O(N^2)
# 树状数组的思想是先对sums进行去重排序得到长为m的序列,然后构建一个与该序列等长的树状数组来记录每个值出现的次数
# 于是,要计算落在区间[sumi - upper, sumi - lower]的sumj的个数,只需对该数组求区间和
# 可行性的原因是,在for sumi in sums循环中,树状数组是随着sumi不断生成的,当前树状数组只记录了(x, x<=i)的sumx
# 对于每个sumi,算法复杂度为O(logN),总的算法复杂度为O(NlogN)
import bisect
class Solution(object):
    def countRangeSum(self, nums, lower, upper):
        """
        :type nums: List[int]
        :type lower: int
        :type upper: int
        :rtype: int
        """
        ans = 0
        sums = [0 for _ in nums]
        for i, num in enumerate(nums):
            if i == 0:
                sums[i] = num
            else:
                sums[i] = sums[i - 1] + num
        oscums = sorted(set(sums))
        ft = FenwickTree(len(oscums))
        for sumi in sums:
            left = bisect.bisect_left(oscums, sumi - upper)
            right = bisect.bisect_right(oscums, sumi - lower)
            ans += ft.getSum(right) - ft.getSum(left) + (lower <= sumi <= upper)
            ft.add(bisect.bisect_right(  oscums, sumi), 1)
        return ans
class FenwickTree(object):
    def __init__(self, n):
        # n 为数组的长度
        self.n = n
        self.c = [0 for _ in range(n + 1)]
    def lowBit(self, x):
        return x & (x ^ (x - 1))
    def add(self, i, val):
        while i <= self.n:
            self.c[i] += val
            i += self.lowBit(i)
    def getSum(self, i):
        sum = 0
        while i > 0:
            sum += self.c[i]
            i -= self.lowBit(i)
        return sum

nums = [-2, 5, -1]
lower = -2
upper = 2
X =Solution()
print(X.countRangeSum(nums, lower, upper))

参考博文:

http://bookshadow.com/weblog/2016/01/11/leetcode-count-of-range-sum/

leetcode327 Count of Range Sum的更多相关文章

  1. Leetcode327: Count of Range Sum 范围和个数问题

    ###问题描述 给定一个整数数组,返回range sum 落在给定区间[lower, upper] (包含lower和upper)的个数.range sum S(i, j) 表示数组中第i 个元素到j ...

  2. 【算法之美】你可能想不到的归并排序的神奇应用 — leetcode 327. Count of Range Sum

    又是一道有意思的题目,Count of Range Sum.(PS:leetcode 我已经做了 190 道,欢迎围观全部题解 https://github.com/hanzichi/leetcode ...

  3. 327. Count of Range Sum

    /* * 327. Count of Range Sum * 2016-7-8 by Mingyang */ public int countRangeSum(int[] nums, int lowe ...

  4. [Swift]LeetCode327. 区间和的个数 | Count of Range Sum

    Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive.Ra ...

  5. [LeetCode] Count of Range Sum 区间和计数

    Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive.Ra ...

  6. LeetCode Count of Range Sum

    原题链接在这里:https://leetcode.com/problems/count-of-range-sum/ 题目: Given an integer array nums, return th ...

  7. leetcode@ [327] Count of Range Sum (Binary Search)

    https://leetcode.com/problems/count-of-range-sum/ Given an integer array nums, return the number of ...

  8. 【LeetCode】327. Count of Range Sum

    题目: Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusiv ...

  9. 327. Count of Range Sum(inplace_marge)

    Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive.Ra ...

随机推荐

  1. MyBatis-注解方式整合SSM

    Spring.Spring MVC.MyBatis 整合 一.依赖 <?xml version="1.0" encoding="UTF-8"?> & ...

  2. 11、JDBC-Druid

    依赖 pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns=" ...

  3. Linux记录-JMX监控JAVA进程

    3.修改xxx.sh 加入export JAVA_OPTS="-Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.mana ...

  4. Sass map详解

    作为一个CSS预处理器,Sass正受到越来越多的青睐,诸如Github.Codepen.CSS-Tricks.SitePoint.w3cplus等网站采用Sass组织.管理CSS文件,Sass正在逐渐 ...

  5. webapi快速开发框架

    一.webapi快速开发框架搭建之后台 从0开始搭建webapi基本框架.权限控制.异常管理.日志管理.缓存管理 源码:https://github.com/shengyu-kmust/webapi ...

  6. bootstrap开发微票儿网站首页

    1.html代码 <!DOCTYPE html><html lang="en"><head> <meta charset="UT ...

  7. OPC和DCOM配置

    本文为Java实现OPC通信的一部分 系统:使用win10 64位专业版 PDF文件: 本文,链接:  百度网盘 密码: reht, Win7和Win7_SP1网络OPC配置,链接:  百度网盘 密码 ...

  8. node中glob模块

    glob glob允许使用规则,从而获取对应规则匹配的文件 node的glob模块允许你使用 * 等符号,来写一个glob规则,像在shell里一样,获取匹配对应规则文件 安装 npm install ...

  9. npm i 与 npm install之间的细小区别

    1.用npm i 安装的模块无法用npm uninstall卸载,需要用npm uninstall i命令 2.npm i 会帮助检测与当前node版本最匹配的npm包 版本号,并匹配出来相互依赖的n ...

  10. 动态字段列表实现及List<T>排序

    public class StudentCardSortHelper<T> { //itemName 要排序的字段名 public static List<StudentCard&g ...