大家好,我是 @负雪明烛。👈👈 点击关注,优质题解不间断。
题目大意
让我们实现一个 RecentCounter
类来计算时间窗口为 [t - 3000, t]
之间的请求个数。
可能有些同学看不懂题目,我详细解释一下:
题目的第一个示例为例:
["RecentCounter", "ping", "ping", "ping", "ping"]
[[], [1], [100], [3001], [3002]]
它的含义是,第一行是调用的函数名,第二行是函数的参数,即:
- 调用
RecentCounter()
,没有输入值,表示初始化计数器,时间为 0,记数为 0 - 调用
ping()
,输入值是 1,即查询窗口在[-2999, 1]
之间有多少 ping - 调用
ping()
,输入值是 100,即查询窗口在[-2900, 100]
之间有多少 ping - 调用
ping()
,输入值是 3001,即查询窗口在[1, 3001]
之间有多少 ping - 调用
ping()
,输入值是 3002,即查询窗口在[2, 3002]
之间有多少 ping
它的调用的可视化如下图所示:
解题方法
从上面的图可以看出,我们其实是在用一个大小为 的滑动窗口,在统计窗口内有多少个 ping。
因此,可以用固定时间窗口为 3000 的队列来模拟,每次 ping(t)
的时候,把队列头部已经超过 的所有请求去除。然后看队列中多少元素,就是 之间的请求数。这是大部分题解采用的方法。
我换了一个方法,不使用队列进行模拟。
我使用一个数组,保存所有的请求时间 $t$;当新请求到达时,我使用二分查找大于等于 $t - 3000$ 的第一个位置。因此就可以获得 之间的数字个数。
举个例子:
为什么可以用二分查找呢?
因为题目已经保证了每次调用 ping(t)
的时候, 都是增加的,也就是说如果我们把所有的 t 保存到数组中,那么数组是单调递增的有序数组。另外,二分查找的时间复杂度是 ,题目给出的数据范围是最多调用 ping()
方法 次,因此总的时间复杂度应该是 $O(N * log(N))$,不会超过力扣所限制的 的计算量。
因此可以使用二分查找。
注意我们在数组中查询的不是确切的一个值,而是 的值,注意下面代码中的对普通二分查找的改变。
代码如下:
class RecentCounter:
def __init__(self):
self.nums = []
def ping(self, t):
"""
:type t: int
:rtype: int
"""
self.nums.append(t)
cur_pos = len(self.nums)
prev_pos = bisect.bisect_left(self.nums, t - 3000)
return cur_pos - prev_pos
# Your RecentCounter object will be instantiated and called as such:
# obj = RecentCounter()
# param_1 = obj.ping(t)
class RecentCounter {
public:
RecentCounter() {
}
int ping(int t) {
requests.push_back(t);
int prev = bisectLeft(requests, t - 3000);
return requests.size() - prev;
}
int bisectLeft(vector<int>& requests, int target) {
int left = 0;
int right = requests.size();
// [left, right)
while (left < right) {
int mid = left + (right - left) / 2;
if (requests[mid] >= target) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
private:
vector<int> requests;
};
/**
* Your RecentCounter object will be instantiated and called as such:
* RecentCounter* obj = new RecentCounter();
* int param_1 = obj->ping(t);
*/
class RecentCounter {
ArrayList<Integer> requests;
public RecentCounter() {
requests = new ArrayList<>();
}
public int ping(int t) {
requests.add(t);
int prev = bisectLeft(requests, t - 3000);
return requests.size() - prev;
}
public int bisectLeft(ArrayList<Integer> requests, int target) {
int left = 0;
int right = requests.size();
// [left, right)
while (left < right) {
int mid = left + (right - left) / 2;
if (requests.get(mid) >= target) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
}
/**
* Your RecentCounter object will be instantiated and called as such:
* RecentCounter obj = new RecentCounter();
* int param_1 = obj.ping(t);
*/
复杂度
- 力扣的 Easy 和 Medium 题目一般只考一个知识点,活学活用哇~
我是 @负雪明烛 ,刷算法题 1000 多道,写了 1000 多篇算法题解,收获阅读量 300 万。
关注我,你将不会错过我的精彩动画题解、面试题分享、组队刷题活动,进入主页 @负雪明烛 右侧有刷题组织,从此刷题不再孤单。
- 在刷题的时候,如果你不知道该怎么刷题,可以看 LeetCode 应该怎么刷?
- 如果你觉得题目太多,想在短时间内快速提高,可以看 LeetCode 最经典的 100 道题。
- 送你一份刷题的代码模板:【LeetCode】代码模板,刷题必会
- 我写的 1000 道 LeetCode 题解,都在这里了,免费拿走。