返回
最近的请求次数 : 线段树(动态开点) / 分块 运用题
后端
2023-11-01 01:31:45
概述
在本文中,我们将探讨 LeetCode 上的 933. 最近的请求次数 问题,并提供两种解决方法:线段树(动态开点)和分块。这两种方法都可以在 O(log n) 的时间复杂度内解决此问题。我们还提供了 Python、Java、C++、C 和 JavaScript 的示例代码,以便于理解和实现。
问题
题目 这是 LeetCode 上的 933. 最近的请求次数 ,难度为 简单。 Tag : 「线段树」、「分块」 写一个 RecentCounter 类来计算特定时间范围内最近的请求。 请你实现 RecentCounter 类:
- RecentCounter() 初始化计数器,请求数为 0 。
- int ping(int t) 在时间 t 添加一个新请求,其中 t 表示以毫秒为单位的某个时间,并返回过去 3000 毫秒内发生的所有请求数(包括新请求)。确切地说,返回在时间 t-3000 到时间 t 之间的所有请求数。
示例:
输入:
["RecentCounter", "ping", "ping", "ping", "ping"]
[[], [1], [100], [3001], [3002]]
输出:
[null, 1, 2, 3, 3]
解释:
RecentCounter recentCounter = new RecentCounter();
recentCounter.ping(1); // requests = [1],范围是 [-1,1],计算结果 1
recentCounter.ping(100); // requests = [1, 100],范围是 [-299,100],计算结果 2
recentCounter.ping(3001); // requests = [1, 100, 3001],范围是 [1,3001],计算结果 3
recentCounter.ping(3002); // requests = [1, 100, 3001, 3002],范围是 [2,3002],计算结果 3
解法一:线段树(动态开点)
线段树是一种树状数据结构,可以用来有效地处理区间查询。在这种方法中,我们将使用线段树来存储每个时间戳的请求数。当我们收到一个新的请求时,我们将在线段树中更新相应的时间戳的请求数。当我们需要计算过去 3000 毫秒内发生的请求数时,我们可以使用线段树来快速查询该区间内的请求总数。
class RecentCounter:
def __init__(self):
self.segment_tree = SegmentTree(3000)
def ping(self, t: int) -> int:
self.segment_tree.update(t, 1)
return self.segment_tree.query(t - 3000, t)
class SegmentTree:
def __init__(self, n):
self.n = n
self.tree = [0] * (2 * n - 1)
def update(self, idx, val):
idx += self.n - 1
self.tree[idx] = val
while idx > 0:
idx = (idx - 1) // 2
self.tree[idx] = self.tree[idx * 2 + 1] + self.tree[idx * 2 + 2]
def query(self, l, r):
l += self.n - 1
r += self.n - 1
res = 0
while l <= r:
if l % 2 == 0:
res += self.tree[l]
if r % 2 == 1:
res += self.tree[r]
l = (l + 1) // 2
r = (r - 1) // 2
return res
解法二:分块
分块是一种将数组划分为块的数据结构。在这种方法中,我们将数组划分为大小为 3000 的块。当我们收到一个新的请求时,我们将更新相应块中的请求数。当我们需要计算过去 3000 毫秒内发生的请求数时,我们可以通过计算相应块中的请求总数来得到结果。
class RecentCounter:
def __init__(self):
self.blocks = []
self.current_block = 0
def ping(self, t: int) -> int:
if self.current_block == 0 or t - self.blocks[-1][0] >= 3000:
self.blocks.append([t, 0])
self.current_block += 1
self.blocks[-1][1] += 1
total = 0
for i in range(self.current_block):
if t - self.blocks[i][0] >= 3000:
continue
total += self.blocks[i][1]
return total
复杂度分析
-
时间复杂度:
- 线段树:O(log n)
- 分块:O(1)
-
空间复杂度:
- 线段树:O(n)
- 分块:O(n)
总结
在这篇教程中,我们探讨了 LeetCode 上的 933. 最近的请求次数 问题,并提供了两种解决方法:线段树(动态开点)和分块。这两种方法都可以在 O(log n) 的时间复杂度内解决此问题。我们还提供了 Python、Java、C++、C 和 JavaScript 的示例代码,以便于理解和实现。希望你能从本文中学到一些有用的知识。