Implement a Set with Readable Snapshots
OpenAISoftware Engineer · OnsiteLast updated
Design and implement a versioned set data structure that supports membership modifications, instant snapshotting, and historical point-in-time queries efficiently.
Problem Description
Implement a SnapshotSet data structure that maintains a collection of unique elements while supporting instantaneous snapshots. Users can query current membership as well as historical membership at any previous snapshot.
Implement the SnapshotSet class with the following methods:
__init__(): Initializes the object with an empty set. The initial state before any snapshot is called can be considered epoch0.add(val: int) -> bool: Addsvalto the set. ReturnsTrueifvalwas not already in the set, orFalseif it was already present.remove(val: int) -> bool: Removesvalfrom the set. ReturnsTrueifvalwas in the set, orFalseif it was not present.contains(val: int) -> bool: ReturnsTrueifvalis currently in the set,Falseotherwise.snapshot() -> int: Takes a snapshot of the set's current state and returns a uniquesnapshot_id. The first call tosnapshot()returns0, the second returns1, and so on.contains_at(snapshot_id: int, val: int) -> bool: ReturnsTrueifvalwas present in the set at the timesnapshot_idwas taken,Falseotherwise. Ifsnapshot_idis invalid, raise anIndexError.
Your solution should avoid deep-copying the entire set on each snapshot() call. Aim for O(1) snapshot creation and efficient historical queries.
Interface
class SnapshotSet:
def __init__(self):
pass
def add(self, val: int) -> bool:
pass
def remove(self, val: int) -> bool:
pass
def contains(self, val: int) -> bool:
pass
def snapshot(self) -> int:
pass
def contains_at(self, snapshot_id: int, val: int) -> bool:
pass
Example
ss = SnapshotSet()
ss.add(1) # Returns True, set: {1}
ss.add(2) # Returns True, set: {1, 2}
snap0 = ss.snapshot() # snap0 = 0 (captures {1, 2})
ss.remove(1) # Returns True, set: {2}
ss.add(3) # Returns True, set: {2, 3}
snap1 = ss.snapshot() # snap1 = 1 (captures {2, 3})
ss.contains(1) # Returns False
ss.contains_at(snap0, 1) # Returns True (1 was in snap0)
ss.contains_at(snap1, 1) # Returns False (1 was not in snap1)
ss.contains_at(snap0, 3) # Returns False (3 was added after snap0)
ss.contains_at(snap1, 3) # Returns True
Constraints & Edge Cases
- Total operations: up to
2 * 10^5across all method calls. - Element values:
-10^9 <= val <= 10^9. snapshot_idmust be non-negative and strictly less than the total number of snapshots taken so far.- Multiple additions or removals of the same element within the same snapshot interval should not create redundant history entries.
- Adding an element, removing it, and re-adding it across different snapshots must maintain a correct timeline of intervals.
Hints
Hint 1
Avoid copying the entire set on every snapshot; instead, store a change history or presence timeline per element.
Hint 2
For each value, maintain an ordered record of state changes (or intervals of [added_snap_id, removed_snap_id]) keyed by the snapshot ID.
Hint 3
Use binary search (like `bisect`) over an element's history to answer `contains_at(snapshot_id, val)` in O(log K) time, where K is the number of times that element changed state.
Follow-up questions
- How would you support an `iterate_at(snapshot_id)` operation that returns an iterator over all elements in a given snapshot without copying the set?
- If snapshots can be deleted via `delete_snapshot(snapshot_id)`, how would you reclaim memory and compact historical timelines?
- How would you adapt this design to be thread-safe under high-concurrency read-heavy or write-heavy workloads?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.