150NeetCode 150

33.Time Based Key-Value Store

MediumBinary Search · 6/7

Problem

Design a time-based key-value store that can keep multiple values for the same key at different timestamps and return the value that was current at a given time.

Implement TimeMap:

  • TimeMap() initializes the store.
  • void set(String key, String value, int timestamp) stores value for key at time timestamp.
  • String get(String key, int timestamp) returns the value set for key at the largest timestamp prev with prev <= timestamp. If there is none, return "".

Examples

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:
set("foo", "bar", 1)
get("foo", 1) → "bar"   (exact match at 1)
get("foo", 3) → "bar"   (no value at 3; latest before it is at 1)
set("foo", "bar2", 4)
get("foo", 4) → "bar2"
get("foo", 5) → "bar2"
Input:
["TimeMap", "set", "get", "get"]
[[], ["love", "high", 10], ["love", 5], ["love", 10]]
Output:
[null, null, "", "high"]
Explanation: At time 5 nothing had been stored for "love" yet, so the answer is "".

Constraints

  • 1 <= key.length, value.length <= 100
  • key and value consist of lowercase English letters and digits.
  • 1 <= timestamp <= 10^7
  • All timestamps passed to set are strictly increasing.
  • At most 2 * 10^5 calls will be made to set and get.

💡 Key Insight

Because set is always called with increasing timestamps, simply appending to a per-key list keeps that list sorted by timestamp for free. A get then becomes "find the last entry with time <= timestamp" — a classic upper-bound binary search on that list, in O(log n).

Brute Force (Linear Scan)

Timeset O(1) amortized; get O(n) where n is the number of values stored for that key.SpaceO(N)

Intuition

Store, for each key, a list of (timestamp, value) pairs in insertion order. For get, walk the list backwards and return the first pair whose timestamp is <= timestamp.

Algorithm

  1. set: append (timestamp, value) to map[key].
  2. get: if key is missing return "". Otherwise iterate from the last pair to the first; return the value of the first pair with time <= timestamp.
  3. If none qualifies, return "".

Visualization

map["foo"] = [(1,"bar"), (4,"bar2")]

get("foo", 3):  (4,"bar2") 4 > 3 ✗  →  (1,"bar") 1 ≤ 3 ✓  → "bar"

Code

java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class TimeMap {
    private static class TimeValue {
        final int time;
        final String value;

        TimeValue(int time, String value) {
            this.time = time;
            this.value = value;
        }
    }

    private final Map<String, List<TimeValue>> store;

    public TimeMap() {
        store = new HashMap<>();
    }

    public void set(String key, String value, int timestamp) {
        store.computeIfAbsent(key, k -> new ArrayList<>()).add(new TimeValue(timestamp, value));
    }

    public String get(String key, int timestamp) {
        List<TimeValue> list = store.get(key);
        if (list == null) {
            return "";
        }
        // newest first: the first match is the latest time <= timestamp
        for (int i = list.size() - 1; i >= 0; i--) {
            if (list.get(i).time <= timestamp) {
                return list.get(i).value;
            }
        }
        return "";
    }
}

Complexity

  • Time: set O(1) amortized; get O(n) where n is the number of values stored for that key.
  • Space: O(N) — one entry per set call.

Complexity Comparison

Approach Time Space
Linear Scan set O(1), get O(n) O(N)
TreeMap Floor Lookup set O(log n), get O(log n) O(N)
HashMap + Binary Search set O(1), get O(log n) O(N)

Common Pitfalls

  • Returning null instead of "" when the key is missing or every stored timestamp is larger than the query.
  • Doing an exact-match binary search — the query timestamp often falls between stored timestamps; you need the floor, not equality.
  • Stopping the binary search at the first time <= timestamp hit — there may be a later valid entry to its right; keep moving lo = mid + 1.
  • Using TreeMap.floorKey and then a second get — it works but does two O(log n) lookups; floorEntry returns both at once.
  • Relying on the sorted-append trick when timestamps are not guaranteed to increase — then you need the TreeMap version.