Time Based Key-Value Store medium
Problem Statement
Design a time-based key-value data structure that can store multiple values for the same key at different time stamps. Implement the TimeMap
class:
TimeMap()
: Initializes the object.set(key: string, value: string, timestamp: number)
: Stores the keykey
with the valuevalue
at the given timetimestamp
.get(key: string, timestamp: string)
: Returns a value such that it was stored at a timestamp equal to or smaller thantimestamp
, among all values associated withkey
. Returns "" if there is no such value.
Example 1
Input: inputs = ["TimeMap","set","get","get","set","get","get"], inputs = [[],["foo","bar",1],["foo",1],["foo",3],["foo","bar2",4],["foo",4],["foo",5]]
Output: [null,"bar","bar","bar","bar2","bar2","bar2"]
Explanation:
TimeMap kv;
kv.set("foo", "bar", 1); // store the key "foo" and value "bar" along with timestamp = 1
kv.get("foo", 1); // output "bar"
kv.get("foo", 3); // output "bar" since there is no value corresponding to foo at timestamp 3 and timestamp 2, then the only value is at timestamp 1 ie "bar"
kv.set("foo", "bar2", 4);
kv.get("foo", 4); // output "bar2"
kv.get("foo", 5); //output "bar2"
Example 2
Input: inputs = ["TimeMap","set","set","get","get","get"], inputs = [[],["love","high",10],["love","low",20],["love",5],["love",10],["love",15]]
Output: [null,"","high","high"]
Steps
-
Data Structure: We'll use a
Map
to store the key-value pairs. For each key, the value will be an array of[timestamp, value]
pairs, sorted by timestamp. This allows for efficient binary search. -
set(key, value, timestamp)
: Append the new[timestamp, value]
pair to the array associated with thekey
. No sorting is needed at this point, since we'll handle sorting duringget
. -
get(key, timestamp)
:- If the key doesn't exist, return "".
- Perform a binary search on the sorted array of
[timestamp, value]
pairs to find the largest timestamp less than or equal to the giventimestamp
. - Return the corresponding value; if no such timestamp is found (binary search returns -1), return "".
Explanation
The key to efficiency here is using a Map
for fast key lookups and a sorted array for each key to enable efficient binary search in get
. A binary search has a time complexity of O(log n), where n is the number of entries for a given key. This is significantly better than a linear search.
Code
class TimeMap {
private map: Map<string, [number, string][]>;
constructor() {
this.map = new Map();
}
set(key: string, value: string, timestamp: number): void {
if (!this.map.has(key)) {
this.map.set(key, []);
}
this.map.get(key)!.push([timestamp, value]);
}
get(key: string, timestamp: number): string {
if (!this.map.has(key)) {
return "";
}
const pairs = this.map.get(key)!;
let low = 0;
let high = pairs.length - 1;
let result = "";
while (low <= high) {
const mid = Math.floor((low + high) / 2);
if (pairs[mid][0] <= timestamp) {
result = pairs[mid][1];
low = mid + 1;
} else {
high = mid - 1;
}
}
return result;
}
}
Complexity
-
Time Complexity:
set
: O(1) on average (amortized O(1) due to potential array resizing)get
: O(log n), where n is the number of values associated with the given key. This is due to the binary search.
-
Space Complexity: O(m * n), where m is the number of unique keys, and n is the maximum number of values associated with a single key. This is because we store all the timestamp-value pairs.