Alice frequently takes exams and wants to track her scores and calculate the total scores over specific time periods.
Implement the ExamTracker class:
ExamTracker(): Initializes the ExamTracker object.void record(int time, int score): Alice takes a new exam at time time and achieves the score score.long long totalScore(int startTime, int endTime): Returns an integer that represents the total score of all exams taken by Alice between startTime and endTime (inclusive). If there are no recorded exams taken by Alice within the specified time interval, return 0.It is guaranteed that the function calls are made in chronological order. That is,
record() will be made with strictly increasing time.record() is called with time = t, then totalScore() will always be called with startTime <= endTime <= t.
Example 1:
Input:
["ExamTracker", "record", "totalScore", "record", "totalScore", "totalScore", "totalScore", "totalScore"]
[[], [1, 98], [1, 1], [5, 99], [1, 3], [1, 5], [3, 4], [2, 5]]
Output:
[null, null, 98, null, 98, 197, 0, 99]
Explanation
ExamTracker examTracker = new ExamTracker();98 + 99 = 197.
Constraints:
1 <= time <= 1091 <= score <= 1091 <= startTime <= endTime <= t, where t is the value of time from the most recent call of record().record() will be made with strictly increasing time.ExamTracker(), the first function call will always be record().105 calls will be made in total to record() and totalScore().Problem Overview: You need to design a system that tracks exam scores and efficiently answers queries about score ranges or aggregated statistics. The challenge is supporting repeated queries without scanning the entire dataset every time.
Approach 1: Brute Force Range Scan (Time: O(n) per query, Space: O(1))
The most direct approach stores all scores in an array. For every query, iterate through the entire array and compute the required statistic such as total scores, counts, or averages within a given range. This works because the array already contains all information needed to answer the query. However, each query requires scanning the full list, which becomes expensive when the number of scores or queries grows large. This approach is useful for understanding the problem but does not scale well.
Approach 2: Sorted Array + Prefix Sum (Time: O(n log n) preprocessing, O(log n + 1) query, Space: O(n))
Instead of repeatedly scanning the array, first sort the scores. Then build a prefix sum array where prefix[i] stores the cumulative total up to index i. Sorting allows efficient boundary discovery using binary search. For a query range, locate the left and right indices with binary search and subtract prefix values to compute the result instantly. This eliminates repeated linear scans and reduces query cost dramatically.
Approach 3: Prefix Sum + Binary Search (Time: O(log n) per query, Space: O(n))
The optimal design maintains scores in a sorted structure and precomputes prefix sums. When a query arrives, perform two binary searches to find the first index >= lower bound and the last index <= upper bound. With those indices, compute the aggregated score using a simple prefix subtraction. Binary search ensures logarithmic lookup time, while prefix sums provide constant-time range aggregation. This approach leverages properties of a sorted array and is the standard technique for repeated range queries.
Recommended for interviews: Start by explaining the brute force scan to demonstrate understanding of the requirement. Then move to the Prefix Sum + Binary Search approach. Interviewers expect this optimization because it reduces query cost from O(n) to O(log n) while keeping the implementation simple and predictable.
We use an array times to store the time points of each exam, and another array pre to store the prefix sums, where pre[i] represents the total score of the first i exams. For each call to \texttt{record}(time, score), we add time to times and add the last element of pre plus score to pre.
For each call to \texttt{totalScore}(startTime, endTime), we use binary search to find the first position l in times that is greater than or equal to startTime and the first position r that is greater than endTime, then return pre[r-1] - pre[l-1].
The time complexity is O(log n), where n is the number of exams. The space complexity is O(n).
Python
Java
C++
Go
TypeScript
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Brute Force Range Scan | O(n) per query | O(1) | Small datasets or when queries are rare |
| Sorted Array + Prefix Sum | O(n log n) preprocessing, O(log n) query | O(n) | When scores are static and many range queries are expected |
| Prefix Sum + Binary Search | O(log n) per query | O(n) | Best for frequent queries requiring fast range aggregation |
Leetcode Biweekly Contest 167 Editorials | Maximum Partition Factor | Design Exam Scores Tracker • Abhinav Awasthi • 1,478 views views
Watch 8 more video solutions →Practice Design Exam Scores Tracker with our built-in code editor and test cases.
Practice on FleetCode