Design a hit counter which counts the number of hits received in the past 5 minutes (i.e., the past 300 seconds). Implement the HitCounter
class:
HitCounter()
Initializes the object of the hit counter system.void hit(int timestamp)
Records a hit that happened at timestamp
(in seconds). Several hits may happen at the same same timestamp.int getHits(int timestamp)
Returns the number of hits in the past 5 minutes from timestamp
(i.e., the past 300 seconds).
You should assume that timestamp
is monotonically increasing. You may assume that the earliest timestamp starts at 1.
Implement the HitCounter
class:
class HitCounter {
public:
HitCounter() {}
void hit(int timestamp) {}
int getHits(int timestamp) {}
};
Example 1:
Input: ["HitCounter", "hit", "hit", "hit", "getHits", "hit", "getHits", "getHits"] [[], [1], [2], [3], [4], [300], [300], [301]] Output: [null, null, null, null, 3, null, 4, 3] Explanation: HitCounter hitCounter = new HitCounter(); hitCounter.hit(1); // hit at timestamp 1. hitCounter.hit(2); // hit at timestamp 2. hitCounter.hit(3); // hit at timestamp 3. hitCounter.getHits(4); // get hits at timestamp 4, return 3. hitCounter.hit(300); // hit at timestamp 300. hitCounter.getHits(300); // get hits at timestamp 300, return 4. hitCounter.getHits(301); // get hits at timestamp 301, return 3.
Constraints:
1 <= timestamp <= 2 * 109
hit
will be called at most 3 * 105
times.getHits
will be called at most 3 * 105
times.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The goal is to track website hits over time. The brute force way is to simply remember every single hit that happens, regardless of how old it is, and then when asked for the hit count, go through all the hits and filter them.
Here's how the algorithm would work step-by-step:
class HitCounter:
def __init__(self):
self.hit_timestamps = []
def hit(self, timestamp: int) -> None:
# Store the timestamp of the new hit.
self.hit_timestamps.append(timestamp)
def getHits(self, timestamp: int) -> int:
hits_in_last_300_seconds = 0
# Iterate through all recorded timestamps.
for hit_timestamp in self.hit_timestamps:
# Check if the hit occurred within the last 300 seconds.
if timestamp - hit_timestamp < 300:
hits_in_last_300_seconds += 1
return hits_in_last_300_seconds
The best way to count recent hits is to use a data structure that automatically forgets old information. We want to quickly add new hits and efficiently count only the hits that have occurred within the last five minutes.
Here's how the algorithm would work step-by-step:
class HitCounter:
def __init__(self):
self.hit_timestamps = []
def hit(self, timestamp: int) -> None:
self.hit_timestamps.append(timestamp)
def getHits(self, timestamp: int) -> int:
# Remove outdated timestamps from the list.
while self.hit_timestamps and self.hit_timestamps[0] <= timestamp - 300:
self.hit_timestamps.pop(0)
# The remaining timestamps represent hits in the last 5 minutes.
return len(self.hit_timestamps)
# Your HitCounter object will be instantiated and called as such:
# hit_counter = HitCounter()
# hit_counter.hit(timestamp)
# hits = hit_counter.getHits(timestamp)
Case | How to Handle |
---|---|
Timestamp is non-positive (zero or negative) | Treat non-positive timestamps as invalid inputs and either throw an exception, ignore the hit, or assume it's a very old event (timestamp 0), depending on requirements. |
Timestamp is smaller than the previous timestamp in hit() | Handle out-of-order timestamps gracefully by either ignoring the hit, throwing an exception to indicate invalid input, or adjusting the internal data structure to maintain chronological order as best as possible. |
Large number of hits within a very short time frame, potentially exceeding memory or computational limits | Employ a data structure optimized for space (e.g., a circular buffer) and consider aggregation techniques or sampling if necessary to manage high hit rates. |
getHits() is called with a timestamp that is much earlier than any recorded hits | Return 0 in `getHits()` when the provided timestamp is earlier than any recorded hits, as no hits could have occurred within the last 5 minutes from that point. |
Integer overflow in timestamp representation or hit counts | Use appropriate data types (e.g., long) to prevent integer overflows when storing timestamps and hit counts, and consider using a big integer library if necessary for extremely large values. |
getHits() and hit() are called concurrently from multiple threads | Use thread-safe data structures and synchronization mechanisms (e.g., mutexes, locks) to ensure data consistency and prevent race conditions when `getHits()` and `hit()` are accessed concurrently. |
The range of valid timestamps is very large leading to huge memory consumption | Use circular queue or optimized data structure such as hash map with expiration to limit the data held in memory for better scalability. |
No hits have been recorded yet | Return 0 in `getHits()` when no hits have been recorded, which is the initial state of the HitCounter. |