PracHub
QuestionsPremiumCoachesLearningGuidesInterview Prep

Quick Overview

This question evaluates data structure design, versioning, and iterator consistency concepts within the Coding & Algorithms domain, focusing on implementing a mutable set that supports snapshot iterators.

  • medium
  • Databricks
  • Coding & Algorithms
  • Software Engineer

Implement a Snapshot Set Iterator

Company: Databricks

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Technical Screen

Design and implement a mutable `SnapshotSet` data structure. The set stores unique keys and supports normal set operations, plus snapshot iterators. Implement the following API: ```text class SnapshotSet: add(key) -> None remove(key) -> None contains(key) -> bool iterator() -> SnapshotIterator class SnapshotIterator: has_next() -> bool next() -> key ``` Semantics: - `add(key)` inserts `key` into the current set if it is not already present. - `remove(key)` removes `key` from the current set if it is present. - `contains(key)` returns whether `key` is currently present. - `iterator()` returns an iterator over a frozen snapshot of the set at the exact time the iterator is created. - Mutations after `iterator()` is called must not affect that iterator. - Multiple iterators may exist at the same time, each with its own snapshot view. - Each iterator should return every key that existed in its snapshot exactly once and should not return keys that were absent from that snapshot. - The iteration order may be arbitrary unless you choose to define a deterministic order. Discuss and implement an efficient approach. Consider tradeoffs between designs such as maintaining a global mutation log versus maintaining per-key version histories, especially for iterator initialization cost, memory usage, and iteration performance when the mutation log becomes large.

Quick Answer: This question evaluates data structure design, versioning, and iterator consistency concepts within the Coding & Algorithms domain, focusing on implementing a mutable set that supports snapshot iterators.

Design a function-based version of a mutable `SnapshotSet` that supports frozen snapshot iterators. The original object-oriented API is: - `add(key)` - `remove(key)` - `contains(key)` - `iterator() -> SnapshotIterator` - `has_next()` / `next()` on each iterator Because this judge is function-based, you must process a batch of operations. Implement `solution(operations, arguments)` where `operations[i]` is one of: - `add` with `arguments[i] = [key]` - `remove` with `arguments[i] = [key]` - `contains` with `arguments[i] = [key]` - `iterator` with `arguments[i] = []` - `has_next` with `arguments[i] = [iterator_id]` - `next` with `arguments[i] = [iterator_id]` When `iterator` is called, create a new snapshot iterator and return its integer id. Iterator ids start at `0` and increase by `1` each time. Snapshot semantics: - An iterator sees the set exactly as it existed when that iterator was created. - Later `add` or `remove` operations must not affect older iterators. - Multiple iterators may exist at the same time, each with its own frozen view. - Each iterator must return every key in its snapshot exactly once. For deterministic judging, every iterator must return keys in ascending numeric order. Return a list of results, one per operation: - `add` / `remove` -> `None` - `contains` / `has_next` -> `bool` - `iterator` -> the new iterator id - `next` -> the next key from that iterator Discussion note: a full copy of the set at every `iterator()` is correct but can be expensive if many snapshots are created. A global mutation log can make iterator creation cheap, but advancing an iterator may become slow when the log grows large. Per-key histories help with membership over time, but full iteration is still awkward. A versioned ordered structure gives a better balance for large inputs.

Constraints

  • 1 <= len(operations) <= 2 * 10^5
  • Keys are integers in the range [-10^9, 10^9]
  • All iterator ids used by `has_next` and `next` are valid
  • `next(iterator_id)` is only called when that iterator still has at least one remaining key

Examples

Input: (['add','add','iterator','remove','add','contains','has_next','next','has_next','next','has_next','iterator','next','contains'], [[1],[2],[],[2],[1],[2],[0],[0],[0],[0],[0],[],[1],[1]])

Expected Output: [None, None, 0, None, None, False, True, 1, True, 2, False, 1, 1, True]

Explanation: Iterator 0 freezes the snapshot {1, 2}. Removing 2 from the live set and re-adding 1 do not change iterator 0. A later iterator 1 sees the current set {1}.

Input: (['iterator','has_next','add','add','remove','remove','iterator','has_next'], [[],[0],[5],[5],[7],[5],[],[1]])

Expected Output: [0, False, None, None, None, None, 1, False]

Explanation: The first iterator is created on an empty set. Adding a duplicate and removing a missing key are both no-ops. After removing 5, the set is empty again, so the second iterator is also empty.

Input: (['add','add','add','iterator','remove','iterator','add','contains','has_next','next','next','next','has_next','has_next','next','next','has_next','iterator','next','next','next'], [[3],[1],[2],[],[2],[],[0],[2],[0],[0],[0],[0],[0],[1],[1],[1],[1],[],[2],[2],[2]])

Expected Output: [None, None, None, 0, None, 1, None, False, True, 1, 2, 3, False, True, 1, 3, False, 2, 0, 1, 3]

Explanation: Iterator 0 sees {1, 2, 3}. After removing 2, iterator 1 sees {1, 3}. After adding 0, iterator 2 sees {0, 1, 3}. Each iterator keeps its own frozen view.

Input: (['add','add','iterator','remove','add','iterator','contains','next','next','has_next','next','has_next','has_next','next','has_next'], [[-1],[4],[],[-1],[2],[],[-1],[0],[1],[0],[0],[0],[1],[1],[1]])

Expected Output: [None, None, 0, None, None, 1, False, -1, 2, True, 4, False, True, 4, False]

Explanation: Negative keys must still be returned in ascending order. Iterator 0 sees {-1, 4}, while iterator 1 sees {2, 4} after the mutation.

Hints

  1. Copying the whole set into every new iterator works, but it can be too slow when snapshots are frequent. Think about storing immutable versions of the set instead.
  2. If you define iteration order as sorted order, coordinate compression plus a persistent segment tree can support updates and snapshot iteration efficiently.
Last updated: Jun 6, 2026

Loading coding console...

PracHub

Master your tech interviews with 8,500+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.

Related Coding Questions

  • Find the Best Commute Mode - Databricks (medium)
  • Partition a Target String by Source Substrings - Databricks (medium)
  • Design In-Memory QPS Counter - Databricks (medium)
  • Implement Random Connectivity and Grid Routing - Databricks
  • Implement a Snapshot Set Iterator - Databricks (hard)