Level 1 An insertion-ordered set with frozen iterators
A metadata service keeps a set of integer file ids. Readers want to walk the set while writers keep changing it, and a reader must see the set exactly as it was when the walk started.
Implement class SnapshotSet:
add(x: int) -> bool: addx;Trueif it was new,Falseif it was already there (the set is unchanged).remove(x: int) -> bool: removex;Trueif it was there,Falseotherwise.contains(x: int) -> boolsize() -> int: how many elements the set holds now.iterator() -> SnapshotIterator: an iterator over the elements present at this moment.
A SnapshotIterator has:
has_next() -> boolnext() -> int: the next element; raisesStopIterationwhen there are none left.
Rules:
- Elements come out in insertion order: the order of the
addcalls that put them in. Adding an element that is already present doesn't move it. An element that is removed and later added again counts as newly inserted, so it goes to the end. - Later
add/removecalls must not change what an existing iterator produces, whether it has started or not. Several iterators can be in use at once, each with its own snapshot. add,remove,containsandsizemust be O(1) on average.
s = SnapshotSet()
s.add(5); s.add(2); s.add(9)
it = s.iterator()
s.remove(2); s.add(4); s.add(2)
it.next() # 5
it.next() # 2 still in its snapshot
it.next() # 9
it.has_next() # False
it2 = s.iterator() # produces 5, 9, 4, 2
s.add(5) # False, already present, stays first
For this level, iterator() may copy the current elements.
Show hint
A plain dict remembers insertion order and has O(1) insert and delete; del followed by a fresh insert moves a key to the end.