Number of Recent Calls
EasyPrompt
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 timet, wheretrepresents some time in milliseconds, and returns the number of requests that has happened in the past3000milliseconds (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 1:
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 <= 109- Each test case will call
pingwith strictly increasing values oft. - At most
104calls will be made toping.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach involves storing all the timestamps of the requests in a list. For each new ping, we add the new timestamp to the list and then iterate through the entire list to count how many timestamps fall within the required time window [t - 3000, t].
Algorithm
- Initialize an empty list,
requests, in the constructor. - In the
ping(t)method:- Add the new timestamp
tto therequestslist. - Calculate the lower bound of the time window:
lowerBound = t - 3000. - Initialize a counter
countto 0. - Iterate through each
timestampin therequestslist. - If
timestamp >= lowerBound, incrementcount. - Return
count.
- Add the new timestamp
Walkthrough
We use a dynamic array (like ArrayList in Java) to keep a record of all timestamps received so far. When the ping(t) method is called, we first add the new timestamp t to the end of the list. Then, we iterate through every timestamp stored in the list, checking if it is greater than or equal to t - 3000. We count how many such timestamps exist and return the total count.
import java.util.ArrayList;import java.util.List; class RecentCounter { private List<Integer> requests; public RecentCounter() { this.requests = new ArrayList<>(); } public int ping(int t) { this.requests.add(t); int lowerBound = t - 3000; int count = 0; // Iterate through all past requests for (int timestamp : this.requests) { if (timestamp >= lowerBound) { count++; } } return count; }}Complexity
Time
O(N) per `ping` call, where N is the total number of pings made so far. This is because we have to iterate through the entire list of previous requests for every new ping.
Space
O(N), where N is the total number of pings. The list stores every timestamp ever received, so its size grows linearly with the number of calls.
Trade-offs
Pros
Very simple and straightforward to implement.
Easy to understand the logic.
Cons
Inefficient time complexity, as it re-scans old timestamps that are no longer relevant.
Inefficient space complexity, as it never discards old timestamps, leading to ever-increasing memory usage.
This approach uses a queue to store the timestamps of recent requests. Since the timestamps are strictly increasing, we can efficiently remove old, irrelevant timestamps from the front of the queue. The size of the queue then directly gives us the number of recent requests.
Algorithm
- Initialize an empty queue,
requests, in the constructor. - In the
ping(t)method:- Add the new timestamp
tto the tail of therequestsqueue. - Calculate the lower bound of the time window:
lowerBound = t - 3000. - While the queue is not empty and the timestamp at the head of the queue (
requests.peek()) is less thanlowerBound: a. Remove the element from the head of the queue (requests.poll()). - Return the current size of the queue.
- Add the new timestamp
Walkthrough
A queue (like LinkedList or ArrayDeque in Java) is an ideal data structure for this problem. It naturally handles the sliding window of time. The key insight is that since new timestamps t are always increasing, any timestamp that is too old for the current window [t - 3000, t] will also be too old for all future windows. Therefore, we can permanently discard them.
When the ping(t) method is called:
- We add the new timestamp
tto the tail of the queue. - We check the timestamp at the head of the queue. While the head element is less than
t - 3000, it's outside our current window, so we remove (poll) it from the queue. - After removing all the outdated timestamps, the remaining elements in the queue are precisely those within the
[t - 3000, t]range. The size of the queue is our answer.
import java.util.Queue;import java.util.LinkedList; class RecentCounter { private Queue<Integer> requests; public RecentCounter() { this.requests = new LinkedList<>(); } public int ping(int t) { // Add the new request timestamp this.requests.add(t); // Remove requests that are older than t - 3000 while (!this.requests.isEmpty() && this.requests.peek() < t - 3000) { this.requests.poll(); } // The size of the queue is the number of recent requests return this.requests.size(); }}Complexity
Time
Amortized O(1) per `ping` call. Each timestamp is added to the queue once (`add`) and removed from the queue at most once (`poll`). Although a single `ping` call might involve multiple `poll` operations, the total work is proportional to the number of pings, making the average time per ping constant.
Space
O(W), where W is the maximum number of requests that can occur within the 3000ms window. The queue only stores timestamps that are within the recent time frame, so the space usage is bounded by the window size (at most 3001), not the total number of calls.
Trade-offs
Pros
Highly efficient time complexity.
Optimal space complexity, as it only stores relevant data.
The logic elegantly models the sliding window nature of the problem.
Cons
Slightly more complex to reason about than the brute-force list approach, particularly regarding the amortized time complexity.
Solutions
Solution
public class RecentCounter { private Queue < int > q = new Queue < int >(); public RecentCounter () { } public int Ping ( int t ) { q . Enqueue ( t ); while ( q . Peek () < t - 3000 ) { q . Dequeue (); } return q . Count ; } } /** * Your RecentCounter object will be instantiated and called as such: * RecentCounter obj = new RecentCounter(); * int param_1 = obj.Ping(t); */class RecentCounter { private Deque < Integer > q = new ArrayDeque <>(); public RecentCounter () { } public int ping ( int t ) { q . offer ( t ); while ( q . peekFirst () < t - 3000 ) { q . pollFirst (); } return q . size (); } } /** * Your RecentCounter object will be instantiated and called as such: * RecentCounter obj = new RecentCounter(); * int param_1 = obj.ping(t); */var RecentCounter = function () { this . q = []; }; /** * @param {number} t * @return {number} */ RecentCounter . prototype . ping = function ( t ) { this . q . push ( t ); while ( this . q [ 0 ] < t - 3000 ) { this . q . shift (); } return this . q . length ; }; /** * Your RecentCounter object will be instantiated and called as such: * var obj = new RecentCounter() * var param_1 = obj.ping(t) */class RecentCounter { public: deque < int > q ; RecentCounter () { } int ping ( int t ) { q . push_back ( t ); while ( q . front () < t - 3000 ) { q . pop_front (); } return q . size (); } }; /** * Your RecentCounter object will be instantiated and called as such: * RecentCounter* obj = new RecentCounter(); * int param_1 = obj->ping(t); */class RecentCounter : def __init__ ( self ): self . q = deque () def ping ( self , t : int ) -> int : self . q . append ( t ) while self . q [ 0 ] < t - 3000 : self . q . popleft () return len ( self . q ) # Your RecentCounter object will be instantiated and called as such: # obj = RecentCounter() # param_1 = obj.ping(t)Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.