给你一个整数数组 nums
以及两个整数 lower
和 upper
。求数组中,值位于范围 [lower, upper]
(包含 lower
和 upper
)之内的 区间和的个数 。
区间和 S(i, j)
表示在 nums
中,位置从 i
到 j
的元素之和,包含 i
和 j
(i
≤ j
)。
示例 1:
输入:nums = [-2,5,-1], lower = -2, upper = 2 输出:3 解释:存在三个区间:[0,0]、[2,2] 和 [0,2] ,对应的区间和分别是:-2 、-1 、2 。
示例 2:
输入:nums = [0], lower = 0, upper = 0 输出:1
提示:
1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1
-105 <= lower <= upper <= 105
- 题目数据保证答案是一个 32 位 的整数
方法一:树状数组
树状数组,也称作“二叉索引树”(Binary Indexed Tree)或 Fenwick 树。 它可以高效地实现如下两个操作:
- 单点更新
update(x, delta)
: 把序列 x 位置的数加上一个值 delta; - 前缀和查询
query(x)
:查询序列[1,...x]
区间的区间和,即位置 x 的前缀和。
这两个操作的时间复杂度均为
本题中,对于每个下标 j,以 j 为右端点的下标对的数量,就等于 preSum[1..j]
中的所有整数,出现在区间 [preSum[j] - upper, preSum[j] - lower]
的次数。
lower <= preSum[j] - preSum[i - 1] <= upper
,变形得preSum[j] - upper <= preSum[i - 1] <= preSum[j] - lower
。
我们可以用树状数组,从左到右扫描前缀和数组,每遇到一个前缀和 s,就在树状数组中查询区间 [preSum[j] - upper, preSum[j] - lower]
内的整数的数量,随后将 s 更新至树状数组。
方法二:线段树
线段树将整个区间分割为多个不连续的子区间,子区间的数量不超过 log(width)
。更新某个元素的值,只需要更新 log(width)
个区间,并且这些区间都包含在一个包含该元素的大区间内。
- 线段树的每个节点代表一个区间;
- 线段树具有唯一的根节点,代表的区间是整个统计范围,如
[1, N]
; - 线段树的每个叶子节点代表一个长度为 1 的元区间
[x, x]
; - 对于每个内部节点
[l, r]
,它的左儿子是[l, mid]
,右儿子是[mid + 1, r]
, 其中mid = ⌊(l + r) / 2⌋
(即向下取整)。
Python3 代码 TLE,Java 代码 AC。
树状数组:
class BinaryIndexedTree:
def __init__(self, n):
self.n = n
self.c = [0] * (n + 1)
@staticmethod
def lowbit(x):
return x & -x
def update(self, x, delta):
while x <= self.n:
self.c[x] += delta
x += BinaryIndexedTree.lowbit(x)
def query(self, x):
s = 0
while x > 0:
s += self.c[x]
x -= BinaryIndexedTree.lowbit(x)
return s
class Solution:
def countRangeSum(self, nums: List[int], lower: int, upper: int) -> int:
presum = [0]
for v in nums:
presum.append(presum[-1] + v)
alls = set()
for s in presum:
alls.add(s)
alls.add(s - lower)
alls.add(s - upper)
alls = sorted(alls)
m = {v: i for i, v in enumerate(alls, 1)}
tree = BinaryIndexedTree(len(m))
ans = 0
for s in presum:
i, j = m[s - upper], m[s - lower]
ans += tree.query(j) - tree.query(i - 1)
tree.update(m[s], 1)
return ans
线段树:
class Node:
def __init__(self):
self.l = 0
self.r = 0
self.v = 0
class SegmentTree:
def __init__(self, n):
self.tr = [Node() for _ in range(4 * n)]
self.build(1, 1, n)
def build(self, u, l, r):
self.tr[u].l = l
self.tr[u].r = r
if l == r:
return
mid = (l + r) >> 1
self.build(u << 1, l, mid)
self.build(u << 1 | 1, mid + 1, r)
def modify(self, u, x, v):
if self.tr[u].l == x and self.tr[u].r == x:
self.tr[u].v += v
return
mid = (self.tr[u].l + self.tr[u].r) >> 1
if x <= mid:
self.modify(u << 1, x, v)
else:
self.modify(u << 1 | 1, x, v)
self.pushup(u)
def pushup(self, u):
self.tr[u].v = self.tr[u << 1].v + self.tr[u << 1 | 1].v
def query(self, u, l, r):
if self.tr[u].l >= l and self.tr[u].r <= r:
return self.tr[u].v
mid = (self.tr[u].l + self.tr[u].r) >> 1
v = 0
if l <= mid:
v += self.query(u << 1, l, r)
if r > mid:
v += self.query(u << 1 | 1, l, r)
return v
class Solution:
def countRangeSum(self, nums: List[int], lower: int, upper: int) -> int:
s = [0]
for x in nums:
s.append(s[-1] + x)
alls = set()
for v in s:
alls.add(v)
alls.add(v - lower)
alls.add(v - upper)
m = {v: i for i, v in enumerate(sorted(alls), 1)}
tree = SegmentTree(len(m))
ans = 0
for v in s:
l, r = m[v - upper], m[v - lower]
ans += tree.query(1, l, r)
tree.modify(1, m[v], 1)
return ans
树状数组:
class Solution {
public int countRangeSum(int[] nums, int lower, int upper) {
int n = nums.length;
long[] preSum = new long[n + 1];
for (int i = 0; i < n; ++i) {
preSum[i + 1] = preSum[i] + nums[i];
}
TreeSet<Long> ts = new TreeSet<>();
for (long s : preSum) {
ts.add(s);
ts.add(s - upper);
ts.add(s - lower);
}
Map<Long, Integer> m = new HashMap<>();
int idx = 1;
for (long s : ts) {
m.put(s, idx++);
}
int ans = 0;
BinaryIndexedTree tree = new BinaryIndexedTree(m.size());
for (long s : preSum) {
int i = m.get(s - upper);
int j = m.get(s - lower);
ans += tree.query(j) - tree.query(i - 1);
tree.update(m.get(s), 1);
}
return ans;
}
}
class BinaryIndexedTree {
private int n;
private int[] c;
public BinaryIndexedTree(int n) {
this.n = n;
c = new int[n + 1];
}
public void update(int x, int delta) {
while (x <= n) {
c[x] += delta;
x += lowbit(x);
}
}
public int query(int x) {
int s = 0;
while (x > 0) {
s += c[x];
x -= lowbit(x);
}
return s;
}
public static int lowbit(int x) {
return x & -x;
}
}
线段树:
class Solution {
public int countRangeSum(int[] nums, int lower, int upper) {
int n = nums.length;
long[] preSum = new long[n + 1];
for (int i = 0; i < n; ++i) {
preSum[i + 1] = preSum[i] + nums[i];
}
TreeSet<Long> ts = new TreeSet<>();
for (long s : preSum) {
ts.add(s);
ts.add(s - upper);
ts.add(s - lower);
}
Map<Long, Integer> m = new HashMap<>();
int idx = 1;
for (long s : ts) {
m.put(s, idx++);
}
int ans = 0;
SegmentTree tree = new SegmentTree(m.size());
for (long s : preSum) {
int l = m.get(s - upper);
int r = m.get(s - lower);
ans += tree.query(1, l, r);
tree.modify(1, m.get(s), 1);
}
return ans;
}
}
class Node {
int l;
int r;
int v;
}
class SegmentTree {
private Node[] tr;
public SegmentTree(int n) {
tr = new Node[4 * n];
for (int i = 0; i < tr.length; ++i) {
tr[i] = new Node();
}
build(1, 1, n);
}
public void build(int u, int l, int r) {
tr[u].l = l;
tr[u].r = r;
if (l == r) {
return;
}
int mid = (l + r) >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
public void modify(int u, int x, int v) {
if (tr[u].l == x && tr[u].r == x) {
tr[u].v += v;
return;
}
int mid = (tr[u].l + tr[u].r) >> 1;
if (x <= mid) {
modify(u << 1, x, v);
} else {
modify(u << 1 | 1, x, v);
}
pushup(u);
}
public void pushup(int u) {
tr[u].v = tr[u << 1].v + tr[u << 1 | 1].v;
}
public int query(int u, int l, int r) {
if (tr[u].l >= l && tr[u].r <= r) {
return tr[u].v;
}
int mid = (tr[u].l + tr[u].r) >> 1;
int v = 0;
if (l <= mid) {
v += query(u << 1, l, r);
}
if (r > mid) {
v += query(u << 1 | 1, l, r);
}
return v;
}
}
class BinaryIndexedTree {
public:
int n;
vector<int> c;
BinaryIndexedTree(int _n)
: n(_n)
, c(_n + 1) { }
void update(int x, int delta) {
while (x <= n) {
c[x] += delta;
x += lowbit(x);
}
}
int query(int x) {
int s = 0;
while (x > 0) {
s += c[x];
x -= lowbit(x);
}
return s;
}
int lowbit(int x) {
return x & -x;
}
};
class Solution {
public:
int countRangeSum(vector<int>& nums, int lower, int upper) {
int n = nums.size();
vector<long long> preSum(n + 1);
for (int i = 0; i < n; ++i) preSum[i + 1] = preSum[i] + nums[i];
set<long long> alls;
for (auto& s : preSum) {
alls.insert(s);
alls.insert(s - upper);
alls.insert(s - lower);
}
unordered_map<long long, int> m;
int idx = 1;
for (auto& v : alls) m[v] = idx++;
BinaryIndexedTree* tree = new BinaryIndexedTree(m.size());
int ans = 0;
for (auto& s : preSum) {
int i = m[s - upper], j = m[s - lower];
ans += tree->query(j) - tree->query(i - 1);
tree->update(m[s], 1);
}
return ans;
}
};
type BinaryIndexedTree struct {
n int
c []int
}
func newBinaryIndexedTree(n int) *BinaryIndexedTree {
c := make([]int, n+1)
return &BinaryIndexedTree{n, c}
}
func (this *BinaryIndexedTree) lowbit(x int) int {
return x & -x
}
func (this *BinaryIndexedTree) update(x, delta int) {
for x <= this.n {
this.c[x] += delta
x += this.lowbit(x)
}
}
func (this *BinaryIndexedTree) query(x int) int {
s := 0
for x > 0 {
s += this.c[x]
x -= this.lowbit(x)
}
return s
}
func countRangeSum(nums []int, lower int, upper int) int {
n := len(nums)
presum := make([]int, n+1)
for i, v := range nums {
presum[i+1] = presum[i] + v
}
alls := make(map[int]bool)
for _, s := range presum {
alls[s] = true
alls[s-upper] = true
alls[s-lower] = true
}
var t []int
for s, _ := range alls {
t = append(t, s)
}
sort.Ints(t)
m := make(map[int]int)
for i, v := range t {
m[v] = i + 1
}
ans := 0
tree := newBinaryIndexedTree(len(alls))
for _, s := range presum {
i, j := m[s-upper], m[s-lower]
ans += tree.query(j) - tree.query(i-1)
tree.update(m[s], 1)
}
return ans
}