leetcode327 Count of Range Sum
问题描述:
给定一个整数数组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的更多相关文章
- Leetcode327: Count of Range Sum 范围和个数问题
###问题描述 给定一个整数数组,返回range sum 落在给定区间[lower, upper] (包含lower和upper)的个数.range sum S(i, j) 表示数组中第i 个元素到j ...
- 【算法之美】你可能想不到的归并排序的神奇应用 — leetcode 327. Count of Range Sum
又是一道有意思的题目,Count of Range Sum.(PS:leetcode 我已经做了 190 道,欢迎围观全部题解 https://github.com/hanzichi/leetcode ...
- 327. Count of Range Sum
/* * 327. Count of Range Sum * 2016-7-8 by Mingyang */ public int countRangeSum(int[] nums, int lowe ...
- [Swift]LeetCode327. 区间和的个数 | Count of Range Sum
Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive.Ra ...
- [LeetCode] Count of Range Sum 区间和计数
Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive.Ra ...
- LeetCode Count of Range Sum
原题链接在这里:https://leetcode.com/problems/count-of-range-sum/ 题目: Given an integer array nums, return th ...
- 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 ...
- 【LeetCode】327. Count of Range Sum
题目: Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusiv ...
- 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 ...
随机推荐
- C语言复习---找出报数最后一人
题意: 有n个人围成一圈 顺序排号 从第1个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位. 算法实现: (一)一种是按照链表数据结构(一)线性表循环链表之约瑟夫环 ...
- scrapy框架使用教程
scrapy框架真的是很强大.非常值得学习一下.本身py就追求简洁,所以本身代码量很少却能写出很强大的功能.对比java来说.不过py的语法有些操蛋,比如没有智能提示.动态语言的通病.我也刚学习不到1 ...
- mysql安装绑定my.ini
Windows操作系统中,我们安装Mysql有两个选择: 一是下载MSI点击运行,利用windows系统安装程序的方法按部就班的来安装: 二是下载ZIP,解压出来就能立即使用. 在使用ZIP安装时,安 ...
- python 内置函数,匿名函数,sorted,filter,map,递归,二分法,冒泡算法 eval
############################总结#################################1. lambda 匿名函数 语法——lambda 参数:返回值 __na ...
- DirectX11 With Windows SDK--03 索引缓冲区、常量缓冲区
前言 一个立方体有8个顶点,然而绘制一个立方体需要画12个三角形,如果按照前面的方法绘制的话,则需要提供36个顶点,而且这里面的顶点数据会重复4次甚至5次.这样的绘制方法会占用大量的内存空间. 接下来 ...
- python matplotlib 库学习
基本使用 import matplotlib.pyplot as plt import numpy as np x = np.linspace(-1,1,50) y = 2*x+1 plt.figur ...
- 01-Unity深入浅出(一)
一. 温故而知新 在开始学习Unity框架之前,有必要温习一下 [依赖倒置原则]和[手写IOC], 因为我们框架代码的构建都是基于[依赖倒置原则]的,而Unity框架的核心思想就是IOC和DI,所以有 ...
- 404.17 - 动态内容通过通配符 MIME 映射映射到静态文件处理程序
刚刚重装了系统,原有的ASP.NET工程下面的WebService无法运行,如下: 404.17 - 动态内容通过通配符 MIME 映射映射到静态文件处理程序 微软的提示,是做三项更改,但是我改了之后 ...
- 三十九、Linux 线程——线程的同步和互斥
39.1 概念 线程同步 是一个宏观概念,在微观上包含线程的相互排斥和线程先后执行的约束问题 解决同步方式 条件变量 线程信号量 线程互斥 线程执行的相互排斥 解决互斥的方式 互斥锁 读写锁 线程信号 ...
- Ubuntu中在服务器和本机之间传递文件
首先可以通过root进入到服务器中,(登录方法在下面讲解)为自己创建一个用户. useradd的选项: 选项: -b, --base-dir BASE_DIR 新账户的主目录的基目录 -c, --co ...