33. Time Based Key-Value Store
View on NeetCode
View on LeetCode
Problem
Design a time-based key-value data structure that can store multiple values for the same key at different time stamps and retrieve the key’s value at a certain timestamp.
Implement the TimeMap class:
TimeMap()Initializes the object of the data structure.void set(String key, String value, int timestamp)Stores the keykeywith the valuevalueat the given timetimestamp.String get(String key, int timestamp)Returns a value such thatsetwas called previously, withtimestamp_prev <= timestamp. If there are multiple such values, it returns the value associated with the largesttimestamp_prev. If there are no values, it returns"".
Example 1:
Input:
["TimeMap", "set", "get", "get", "set", "get", "get"]
[[], ["foo", "bar", 1], ["foo", 1], ["foo", 3], ["foo", "bar2", 4], ["foo", 4], ["foo", 5]]
Output:
[null, null, "bar", "bar", null, "bar2", "bar2"]
Explanation:
TimeMap timeMap = new TimeMap();
timeMap.set("foo", "bar", 1); // store the key "foo" and value "bar" along with timestamp = 1.
timeMap.get("foo", 1); // return "bar"
timeMap.get("foo", 3); // return "bar", since there is no value corresponding to foo at timestamp 3 and timestamp 2, then the only value is at timestamp 1 is "bar".
timeMap.set("foo", "bar2", 4); // store the key "foo" and value "bar2" along with timestamp = 4.
timeMap.get("foo", 4); // return "bar2"
timeMap.get("foo", 5); // return "bar2"
Constraints:
1 <= key.length, value.length <= 100keyandvalueconsist of lowercase English letters and digits.1 <= timestamp <= 10^7- All the timestamps
timestampofsetare strictly increasing. - At most
2 * 10^5calls will be made tosetandget.
Solution
Approach: HashMap + Binary Search
The key insight is to store a list of (timestamp, value) pairs for each key. Since timestamps are strictly increasing, the list is naturally sorted. We can use binary search to find the largest timestamp <= the query timestamp.
Data structure:
- HashMap: key → list of (timestamp, value) pairs
- For each key, the list is sorted by timestamp (guaranteed by strictly increasing timestamps)
Implementation
class TimeMap:
def __init__(self):
# key -> list of [timestamp, value]
self.store = {}
def set(self, key: str, value: str, timestamp: int) -> None:
if key not in self.store:
self.store[key] = []
self.store[key].append([timestamp, value])
def get(self, key: str, timestamp: int) -> str:
if key not in self.store:
return ""
values = self.store[key]
left, right = 0, len(values) - 1
result = ""
# Binary search for largest timestamp <= target
while left <= right:
mid = (left + right) // 2
if values[mid][0] <= timestamp:
result = values[mid][1]
left = mid + 1 # Try to find a larger valid timestamp
else:
right = mid - 1
return result
Alternative (Using bisect):
from bisect import bisect_right
class TimeMap:
def __init__(self):
self.store = {}
def set(self, key: str, value: str, timestamp: int) -> None:
if key not in self.store:
self.store[key] = []
self.store[key].append([timestamp, value])
def get(self, key: str, timestamp: int) -> str:
if key not in self.store:
return ""
values = self.store[key]
# Find insertion point for timestamp
i = bisect_right(values, [timestamp, chr(255)])
# Return value at i-1 if it exists
return values[i-1][1] if i > 0 else ""
Complexity Analysis
- Time Complexity:
set: O(1), we append to the listget: O(log n), where n is the number of timestamps for the key. We perform binary search.
- Space Complexity: O(n), where n is the total number of set operations. We store all key-value-timestamp tuples.
Key Insights
-
Natural Sorting: Since timestamps are strictly increasing, we don’t need to sort the list. Each new timestamp is automatically in the correct position when appended.
-
Binary Search Target: We’re looking for the largest timestamp that is less than or equal to the query timestamp. This is a classic “find rightmost” binary search variant.
-
Result Tracking: We track the result during binary search. When we find a valid timestamp (
values[mid][0] <= timestamp), we save it as a potential answer and continue searching right for a larger valid timestamp. -
Empty Result: If we never find a valid timestamp (all timestamps are greater than query timestamp), we return an empty string.
-
bisect Module: Python’s
bisect_rightcan be used to find the insertion point. The value at indexi-1gives us the largest timestamp <= query timestamp.