Time Based Key-Value Store solution and explanation
9 more videos on this problem
Time Based Key-Value Store - Leetcode 981 - PythonNeetCode · 17:16 · 183K views
Time Based Key Value Store | Netflix Coding Question | Binary SearchAlgosWithMichael · 14:21 · 12K views
Time Based Key Value Store (LeetCode 981) | Using Map and composite structureNikhil Lohia · 22:06 · 6K views
Leetcode 981 Time Based Key-Value Store | Coding Decoded SDE SheetCoding Decoded · 9:09 · 2K views
[Java] Leetcode 981. Time Based Key-Value Store [Binary Search #7]Eric Programming · 14:04 · 2K views
Time Based Key-Value Store | LeetCode 981Destination FAANG · 17:17 · 1K views
Time Based Key-Value Store - 981. LeetCode - JavaTeddy Smith · 8:35 · 809 views
Daily Leetcode #919: Oct 06, 2022 - 981. Time Based Key-Value StoreProgramming Live with Larry · 7:36 · 687 views
LeetCode | 981. Time Based Key-Value Store | Binary SearchCompl3x · 14:55 · 237 views
Leetcode | 981. Time Based Key-Value Store | Medium | JavascriptSoftware Interviews Prep · 21:17 · 106 views
The 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 <= 107- All the timestamps
timestampofsetare strictly increasing. - At most
2 * 105calls will be made tosetandget.
Approach overview
Problem Overview: Design a data structure that stores multiple values for the same key, each associated with a timestamp. When you call get(key, timestamp), return the value set for that key at the largest timestamp less than or equal to the given timestamp.
The main challenge is efficient historical lookup. Each key may have many timestamped values, and the query must quickly find the closest earlier timestamp. This naturally leads to combining a hash table for key lookup with binary search over timestamps.
Approach 1: HashMap with Sorted Timestamps (O(log n) get, O(1) set)
Store each key in a hash map where the value is a list of (timestamp, value) pairs. The problem guarantees timestamps are strictly increasing for each key, so every set operation simply appends to the list in O(1) time. For get, retrieve the list for the key and run binary search to find the largest timestamp less than or equal to the query timestamp. Many languages provide built-in helpers like bisect in Python. The binary search runs in O(log n) time where n is the number of values stored for that key, while the overall space complexity is O(n).
This approach works well because it leverages the monotonic timestamp guarantee. Since inserts always happen in increasing order, there is no need to re-sort or rebalance structures. You get constant-time writes and logarithmic-time historical reads.
Approach 2: HashMap with Manual Binary Search (O(log n) get, O(1) set)
The same core structure can be implemented manually when built-in binary search utilities are unavailable. Maintain a hash map from key to a vector or array of (timestamp, value) pairs. Each set appends the pair in O(1). During get, perform a classic binary search over the timestamp array: move the left or right pointer depending on whether the mid timestamp is greater or smaller than the query. Track the best candidate seen so far. The search takes O(log n) time and uses O(n) space for storage.
This version demonstrates deeper understanding of how design problems combine simple data structures with algorithmic primitives. Interviewers often expect you to write the binary search logic yourself in languages like C++ or Java.
Recommended for interviews: The HashMap + binary search design is the expected solution. A brute-force scan through all timestamps would take O(n) per query and does not scale. Showing the optimized approach proves you recognize sorted data patterns and can combine hashing with logarithmic search for efficient queries.
Approach 1: HashMap with Sorted Timestamps
This approach uses a HashMap where each key points to a sorted list of timestamp-value pairs. The 'set' function appends the value along with the timestamp to the list associated with the key. The 'get' function retrieves the value at a particular timestamp by performing a binary search to find the rightmost timestamp less than or equal to the requested timestamp.
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]
i = bisect_right(values, (timestamp, chr(127)))
if i == 0:
return ""
return values[i-1][1]class TimeMap {
constructor() {
this.store = new Map();
}
set(key, value, timestamp) {
if (!this.store.has(key)) {
this.store.set(key, []);
}
this.store.get(key).push({ timestamp, value });
}
get(key, timestamp) {
if (!this.store.has(key)) return "";
let values = this.store.get(key);
let left = 0, right = values.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (values[mid].timestamp <= timestamp) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return right >= 0 ? values[right].value : "";
}
}In the Python solution, we employ the 'bisect_right' function from the 'bisect' module to perform a binary search. The key-value pairs are stored such that each key corresponds to a list of (timestamp, value) tuples. The 'set' method stores the new data point in logarithmic time complexity due to the sorted insertion. The 'get' method uses binary search to efficiently find the largest timestamp less than or equal to the given timestamp.
Time Complexity: O(log n) for the 'get' operation and O(1) for the 'set' operation in terms of inserting, where n is the number of entries per key.
Space Complexity: O(n), where n is the number of entries in the TimeMap.
Approach 2: HashMap with Manual Binary Search
This approach is similar, but instead of using a helper library for the binary search, we implement it manually. This approach provides more control over the binary search process.
import java.util.*;
public class TimeMap {
private Map<String, List<Pair>> store;
public TimeMap() {
store = new HashMap<>();
}
public void set(String key, String value, int timestamp) {
store.putIfAbsent(key, new ArrayList<>());
store.get(key).add(new Pair(timestamp, value));
}
public String get(String key, int timestamp) {
if (!store.containsKey(key)) return "";
List<Pair> pairs = store.get(key);
int left = 0, right = pairs.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (pairs.get(mid).timestamp <= timestamp) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return right >= 0 ? pairs.get(right).value : "";
}
private class Pair {
int timestamp;
String value;
Pair(int timestamp, String value) {
this.timestamp = timestamp;
this.value = value;
}
}
}#include <unordered_map>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
class TimeMap {
unordered_map<string, vector<pair<int, string>>> store;
public:
void set(string key, string value, int timestamp) {
store[key].emplace_back(timestamp, value);
}
string get(string key, int timestamp) {
if (store.find(key) == store.end()) return "";
auto& values = store[key];
int left = 0, right = values.size();
while (left < right) {
int mid = (left + right) / 2;
if (values[mid].first <= timestamp) left = mid + 1;
else right = mid;
}
return right > 0 ? values[right - 1].second : "";
}
};In Java, we utilize a HashMap to store a List of Pair objects for each key. Each Pair object contains a timestamp and its corresponding value. For retrieval, we perform manual binary search logic to find the appropriate timestamp efficiently in logarithmic time.
Time Complexity: O(log n) for the 'get' operation and O(1) for the 'set' operation, where n is the number of timestamps for the key.
Space Complexity: O(n), where n is the number of stored key-value pairs.
Approach 3: Hash Table + Ordered Set (or Binary Search)
We can use a hash table kvt to record key-value pairs, where the key is the string key and the value is an ordered set. Each element in the set is a tuple (timestamp, value), representing the value value corresponding to the key key at the timestamp timestamp.
When we need to query the value corresponding to the key key at the timestamp timestamp, we can use the ordered set to find the largest timestamp timestamp' such that timestamp' leq timestamp, and then return the corresponding value.
In terms of time complexity, for the set operation, since the insertion operation of the hash table has a time complexity of O(1), the time complexity is O(1). For the get operation, since the lookup operation of the hash table has a time complexity of O(1) and the lookup operation of the ordered set has a time complexity of O(log n), the time complexity is O(log n). The space complexity is O(n), where n is the number of set operations.
type TimeMap struct {
ktv map[string][]pair
}
func Constructor() TimeMap {
return TimeMap{map[string][]pair{}}
}
func (this *TimeMap) Set(key string, value string, timestamp int) {
this.ktv[key] = append(this.ktv[key], pair{timestamp, value})
}
func (this *TimeMap) Get(key string, timestamp int) string {
pairs := this.ktv[key]
i := sort.Search(len(pairs), func(i int) bool { return pairs[i].t > timestamp })
if i > 0 {
return pairs[i-1].v
}
return ""
}
type pair struct {
t int
v string
}
/**
* Your TimeMap object will be instantiated and called as such:
* obj := Constructor();
* obj.Set(key,value,timestamp);
* param_2 := obj.Get(key,timestamp);
*/Complexity comparison
| Approach | Complexity |
|---|---|
| HashMap with Sorted Timestamps | Time Complexity: |
| HashMap with Manual Binary Search | Time Complexity: |
| Hash Table + Ordered Set (or Binary Search) | – |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| HashMap with Sorted Timestamps (Built-in Binary Search) | set: O(1), get: O(log n) | O(n) | Best general solution when timestamps are inserted in increasing order and the language provides binary search helpers |
| HashMap with Manual Binary Search | set: O(1), get: O(log n) | O(n) | Preferred in interviews or languages where you implement binary search manually (C++, Java) |
Frequently asked questions
Is Time Based Key-Value Store easy or hard?
Time Based Key-Value Store is rated Medium difficulty on LeetCode. The implementation is straightforward once you recognize that timestamps are sorted, but identifying the need for binary search and designing the data structure correctly is the key challenge.
Time Based Key-Value Store Python/Java solution
Python solutions typically store lists of (timestamp, value) pairs and use the bisect module for binary search. Java implementations store pairs in an ArrayList and implement binary search manually to find the closest valid timestamp.
How to solve Time Based Key-Value Store in O(log n)?
Store each key in a hash map with a sorted list of timestamps and their values. When retrieving a value, run binary search to find the largest timestamp less than or equal to the requested timestamp. This reduces lookup from linear scanning to O(log n).
What is the best approach for Time Based Key-Value Store?
The optimal approach uses a HashMap that maps each key to a list of (timestamp, value) pairs stored in increasing timestamp order. Each set operation appends in O(1) time, and get operations use binary search to find the closest timestamp less than or equal to the query. This results in O(log n) lookup time and O(n) total space.
Is Time Based Key-Value Store asked at Google/Amazon/Meta?
Design problems combining hash maps and binary search frequently appear in interviews at companies like Amazon, Google, and Meta. Variants of time-based storage systems and versioned key-value lookups test both data structure design and search optimization skills.
What data structure is used in Time Based Key-Value Store?
The solution primarily uses a hash table to map each key to its history of values. Each key stores an array or list of timestamp-value pairs, and binary search is used on this sorted list to efficiently locate the correct value.
What is the time complexity of Time Based Key-Value Store?
The set operation runs in O(1) because new values are appended to the timestamp list. The get operation performs binary search over the timestamps for that key, which takes O(log n) time. Space complexity is O(n) for storing all key-value timestamp entries.
