返回

最近的请求次数 : 线段树(动态开点) / 分块 运用题

后端

概述

在本文中,我们将探讨 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 的示例代码,以便于理解和实现。希望你能从本文中学到一些有用的知识。