Implement a Snapshot Set Iterator
Company: Microsoft
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates proficiency in data structure design, iterator semantics, and managing snapshot consistency for mutable collections. It is commonly asked in the Coding & Algorithms domain to assess practical implementation skills and conceptual understanding of producing an iterator that reflects the set's state at the moment iterator() is called, testing practical application with reasoning about immutability, consistency, and complexity.
Constraints
- 0 <= len(operations) <= 10^4
- Element values are integers in the range [-10^9, 10^9]
- Snapshot names are unique across "iterator" operations
- Each "iterate" refers to a previously created snapshot name, and each snapshot name is iterated at most once
- The sum of snapshot sizes over all "iterator" operations is at most 10^5
Examples
Input: ([("add", 5), ("add", 2), ("add", 8), ("remove", 5), ("iterator", "it"), ("add", 1), ("contains", 2), ("remove", 2), ("contains", 2), ("add", 2), ("iterator", "it2"), ("iterate", "it"), ("iterate", "it2")],)
Expected Output: [True, False, [2, 8], [1, 2, 8]]
Explanation: The first iterator snapshots the set after 5 was removed, so it sees {2, 8}. Later changes do not affect it. The second iterator is created after 1 and 2 are both present, so it sees {1, 2, 8}.
Input: ([("remove", 10), ("iterator", "a"), ("iterate", "a"), ("add", 4), ("add", 4), ("contains", 4), ("iterator", "b"), ("remove", 4), ("iterate", "b"), ("contains", 4)],)
Expected Output: [[], True, [4], False]
Explanation: Removing 10 does nothing. Snapshot a is taken on an empty set. Adding 4 twice still leaves one copy. Snapshot b keeps [4] even though 4 is removed later.
Input: ([("add", 1), ("add", 2), ("iterator", "x"), ("remove", 1), ("iterator", "y"), ("add", 1), ("iterator", "z"), ("iterate", "x"), ("iterate", "y"), ("iterate", "z")],)
Expected Output: [[1, 2], [2], [1, 2]]
Explanation: Each iterator captures a different moment in time: x sees {1,2}, y sees {2}, and z sees {1,2} after 1 is added back.
Input: ([("add", -3), ("contains", -3), ("iterator", "s"), ("remove", -3), ("contains", -3), ("iterate", "s"), ("iterator", "t"), ("iterate", "t")],)
Expected Output: [True, False, [-3], []]
Explanation: This checks negative numbers and empty snapshots. Iterator s keeps -3 even after it is removed. Iterator t is created after the set becomes empty.
Hints
- A hash set is enough to maintain the current live set efficiently for add, remove, and contains.
- The key is that an iterator must not depend on the live set after it is created. Store a separate snapshot when the "iterator" operation happens.