Facebook Pixel

Time Based key-Value Store

Medium
LeetCode ↗

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 key key with the value value at the given time timestamp.
  • String get(String key, int timestamp) Returns a value such that set was called previously, with timestamp_prev <= timestamp. If there are multiple such values, it returns the value associated with the largest timestamp_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 <= 100
  • key and value consist of lowercase English letters and digits.
  • 1 <= timestamp <= 107
  • All the timestamps timestamp of set are strictly increasing.
  • At most 2 * 105 calls will be made to set and get.

Explanation

For each key, histories[key] is already sorted by timestamp because set calls come in strictly increasing time order. In get, we need the value with the largest timestamp that is <= queryTimestamp.

That is an "upper bound" binary search: we keep a variable pos for the best valid index seen so far, starting at -1. At each mid: if histories[key][mid][0] <= timestamp, this index is valid, so we save pos = mid and move right (left = mid + 1) to try finding a later valid timestamp. If histories[key][mid][0] > timestamp, this index is too large, so we move left (right = mid - 1).

When the loop ends, pos is the rightmost valid timestamp. If pos == -1, there is no timestamp <= queryTimestamp, so return "". Otherwise return histories[key][pos][1].

Implementation

class TimeMap(object):

    def __init__(self):
        self.histories = dict()

    def set(self, key, value, timestamp):
        if not key in self.histories:
            self.histories[key] = []
        self.histories[key].append([timestamp, value])


    def get(self, key, timestamp):
        if not key in self.histories: return ""
        left, right, pos = 0, len(self.histories[key])-1, -1
        while left <= right:
            mid = (left+right) // 2
            if self.histories[key][mid][0] <= timestamp:
                  left = mid + 1
                  pos = mid
            else:
                  right = mid - 1
        if pos == -1: return ""
        return self.histories[key][pos][1]

Intuition

We want to only store the changes of values for each key chronologically in its own array.

We use a map of string to array histories such that histories[key] is an array that stores pairs (timestamp, value) indicating that key stores value at timestamp.

For the get function, we want to find the value stored in key at timestamp. In histories[key], it is the entry with the greatest timestamp less or equal to the given timestamp.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

In a binary min heap, the minimum element can be found in:


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More