You have a RecentCounter
class which counts the number of recent requests within a certain time frame. Implement the RecentCounter
class:
RecentCounter()
Initializes the counter with zero recent requests.int ping(int t)
Adds a new request at time t
, where t
represents some time in milliseconds, and returns the number of requests that has happened in the past 3000 milliseconds (including the new request). Specifically, return the number of requests that have happened in the inclusive range [t - 3000, t]
.It is guaranteed that every call to ping
uses a strictly larger value of t
than the previous call.
Example:
Input
["RecentCounter", "ping", "ping", "ping", "ping"]
[[], [1], [100], [3001], [3002]]
Output
[null, 1, 2, 3, 3]
Explanation
RecentCounter recentCounter = new RecentCounter();
recentCounter.ping(1); // requests = [1], range is [-2999,1], return 1
recentCounter.ping(100); // requests = [1, 100], range is [-2900,100], return 2
recentCounter.ping(3001); // requests = [1, 100, 3001], range is [1,3001], return 3
recentCounter.ping(3002); // requests = [1, 100, 3001, 3002], range is [2,3002], return 3
Constraints:
1 <= t <= 10^9
ping
with strictly increasing values of t
.10^4
calls will be made to ping
.We are tasked with designing a RecentCounter
class that counts the number of recent requests within a 3000 millisecond time frame. Specifically:
RecentCounter()
constructor initializes the counter with zero requests.ping(int t)
method adds a new request at time t
and returns the number of requests that occurred within the range [t - 3000, t]
. It's guaranteed that t
will always be larger than the previous t
value.A straightforward approach is to store all incoming timestamps in a list. For each ping(t)
call, we add the current timestamp t
to the list and then iterate through the list, counting the timestamps that fall within the range [t - 3000, t]
. Finally, we return the count.
class RecentCounter:
def __init__(self):
self.requests = []
def ping(self, t: int) -> int:
self.requests.append(t)
count = 0
for req_time in self.requests:
if t - 3000 <= req_time <= t:
count += 1
return count
O(N) for each ping()
call, where N is the number of requests stored so far. This is because, in the worst case, we iterate through the entire requests
list.
O(N), where N is the number of requests stored, since the requests
list can grow linearly with the number of calls to ping()
.
We can optimize the solution by using a queue to store the requests. The key idea is to maintain only the relevant requests (those within the last 3000 milliseconds) in the queue. For each ping(t)
call:
t
to the queue.[t - 3000, t]
. In other words, remove elements that are less than t - 3000
.from collections import deque
class RecentCounter:
def __init__(self):
self.requests = deque()
def ping(self, t: int) -> int:
self.requests.append(t)
while self.requests[0] < t - 3000:
self.requests.popleft()
return len(self.requests)
O(1) on average for each ping()
call. Although there's a while
loop, in practice, the number of elements removed from the queue in each ping
call is typically small. Thus the amortized time complexity is closer to O(1).
O(W), where W is the maximum number of requests within a 3000ms window. In the worst case, if requests come in very rapidly, the queue might store all of them within that window, but the size is bounded by the window size, not the total number of pings made.
ping()
is called, the queue will be empty initially, and the result will always be 1. The algorithm handles this correctly.ping()
occur in rapid succession (e.g., ping(1)
, ping(2)
, ping(3)
...), the queue will grow quickly. The algorithm will correctly store all the requests within the 3000ms window.ping()
are far apart, the queue will effectively be cleared each time, storing only the most recent request.