Heap / Priority Queue · Medium
LeetCode 2336: Smallest Number in Infinite Set — solution
Solve LeetCode 2336 with a counter for the untouched tail of the set plus a min-heap (and a hash set) for numbers added back — O(log n) per operation.
Problem in plain words
Design a class that represents the set of all positive integers 1, 2, 3, …. popSmallest() removes and returns the smallest number currently in the set. addBack(num) puts num back into the set — but only if it is not already there.
Example: pop → 1, pop → 2, addBack(1), pop → 1, pop → 3. Calling addBack(5) at any point does nothing, because 5 was never removed.
Intuition
The set always looks like this: a few scattered small numbers that were added back, followed by an infinite, unbroken run next, next + 1, … that nobody has touched yet. Represent the run with a single integer next. Represent the scattered numbers with a min-heap so the smallest is easy to get, plus a hash set to reject duplicates. Every added-back number is smaller than next, so whenever the heap is non-empty its top is the overall minimum.
Approach
- Initialise
next = 1, an empty min-heap and an empty hash set. popSmallest: if the heap is non-empty, pop its top, remove it from the hash set and return it. Otherwise returnnextand increment it.addBack(num): ifnum < nextandnumis not in the hash set, push it to the heap and add it to the set. Ifnum >= next, it is already in the set, so do nothing.
Dry run
| Call | Returns | next | Heap |
|---|---|---|---|
| popSmallest | 1 | 2 | {} |
| popSmallest | 2 | 3 | {} |
| addBack(1) | — | 3 | {1} |
| addBack(1) | — (duplicate) | 3 | {1} |
| popSmallest | 1 | 3 | {} |
| popSmallest | 3 | 4 | {} |
Java solution
class SmallestInfiniteSet {
private int next;
private final PriorityQueue<Integer> heap;
private final Set<Integer> inHeap;
public SmallestInfiniteSet() {
next = 1;
heap = new PriorityQueue<>();
inHeap = new HashSet<>();
}
public int popSmallest() {
if (!heap.isEmpty()) {
int smallest = heap.poll();
inHeap.remove(smallest);
return smallest;
}
return next++;
}
public void addBack(int num) {
if (num < next && inHeap.add(num)) {
heap.offer(num);
}
}
}Python solution
class SmallestInfiniteSet:
def __init__(self):
self.next = 1
self.heap = []
self.in_heap = set()
def popSmallest(self) -> int:
if self.heap:
smallest = heapq.heappop(self.heap)
self.in_heap.remove(smallest)
return smallest
self.next += 1
return self.next - 1
def addBack(self, num: int) -> None:
if num < self.next and num not in self.in_heap:
self.in_heap.add(num)
heapq.heappush(self.heap, num)Complexity
| Measure | Cost | Why |
|---|---|---|
| Time | O(log m) per call | Heap push/pop cost log m, where m is the number of added-back values; the counter path is O(1). |
| Space | O(m) | Only added-back numbers are stored explicitly. |
Edge cases and common mistakes
- Adding back a number that was never removed — it is already in the set; the
num < nextcheck rejects it. - Duplicate `addBack` calls — without the hash set, the same number would sit in the heap twice and be popped twice.
- Storing all numbers up front — pre-filling 1..1000 works under the constraints but misses the point of an "infinite" set; the counter handles any range.
›Could a sorted set replace the heap and hash set?
Yes. A TreeSet in Java (or SortedList in Python) both rejects duplicates and gives the minimum in O(log m), so one structure is enough.
