Smallest Number in Infinite Set solution and explanation
9 more videos on this problem
Smallest Number in Infinite Set | 3 Approaches | (MICROSOFT) | Leetcode 2336 | Live Code 🧑🏻💻codestorywithMIK · 31:08 · 9K views
Smallest Number in Infinite Set | Ordered Set | Priority Queue | Fastest Approach | Leetcode 2336Aryan Mittal · 15:37 · 4K views
Smallest Number in Infinite Set #leetcode #leetcode75Deepti Talesra · 7:11 · 2K views
Weekly Contest 301 | Leetcode 2336 Smallest Number in Infinite Set | Easy Set/HeapsCoding Decoded · 6:07 · 2K views
Smallest Number in Infinite Set - Leetcode 2336 - PythonAlgorithms Casts · 4:23 · 2K views
2336. Smallest Number in Infinite Set | LEETCODE MEDIUM | SETcode Explainer · 3:46 · 2K views
Smallest Number in Infinite Set - Leetcode 2336 - PythonTechError · 5:29 · 1K views
2336. Smallest Number in Infinite Set (Leetcode Medium)Programming Live with Larry · 5:21 · 795 views
Leetcode 2336 Smallest Number in Infinite SetMaxMing · 4:10 · 344 views
LeetCode 2336 – Smallest Number in Infinite Set | C++ Min-HeapLAN Academy · 3:58 · 329 views
The problem
You have a set which contains all positive integers [1, 2, 3, 4, 5, ...].
Implement the SmallestInfiniteSet class:
SmallestInfiniteSet()Initializes the SmallestInfiniteSet object to contain all positive integers.int popSmallest()Removes and returns the smallest integer contained in the infinite set.void addBack(int num)Adds a positive integernumback into the infinite set, if it is not already in the infinite set.
Example 1:
Input
["SmallestInfiniteSet", "addBack", "popSmallest", "popSmallest", "popSmallest", "addBack", "popSmallest", "popSmallest", "popSmallest"]
[[], [2], [], [], [], [1], [], [], []]
Output
[null, null, 1, 2, 3, null, 1, 4, 5]
Explanation
SmallestInfiniteSet smallestInfiniteSet = new SmallestInfiniteSet();
smallestInfiniteSet.addBack(2); // 2 is already in the set, so no change is made.
smallestInfiniteSet.popSmallest(); // return 1, since 1 is the smallest number, and remove it from the set.
smallestInfiniteSet.popSmallest(); // return 2, and remove it from the set.
smallestInfiniteSet.popSmallest(); // return 3, and remove it from the set.
smallestInfiniteSet.addBack(1); // 1 is added back to the set.
smallestInfiniteSet.popSmallest(); // return 1, since 1 was added back to the set and
// is the smallest number, and remove it from the set.
smallestInfiniteSet.popSmallest(); // return 4, and remove it from the set.
smallestInfiniteSet.popSmallest(); // return 5, and remove it from the set.
Constraints:
1 <= num <= 1000- At most
1000calls will be made in total topopSmallestandaddBack.
Approach overview
Problem Overview: You design a data structure that represents an infinite set of positive integers starting from 1. The structure must support two operations: popSmallest(), which removes and returns the smallest available number, and addBack(num), which inserts a previously removed number back into the set.
Approach 1: Priority Queue + Hash Set (O(log n) per operation)
This approach explicitly tracks numbers that were removed and later reinserted. Maintain a min-heap storing values returned through addBack and a hash set to avoid duplicates. Also keep a pointer current representing the smallest number that has never been popped from the infinite sequence. When popSmallest() runs, compare the heap's smallest element with current. If the heap contains a smaller value, remove it from the heap and set; otherwise return current and increment the pointer. When addBack(num) is called, insert num into the heap only if it is smaller than current and not already tracked in the set. Heap operations cost O(log n) time and the set ensures constant-time membership checks. Space complexity is O(n) for stored reinserted values. This design is a classic combination of heap (priority queue) ordering with fast lookups using a hash table.
Approach 2: Indexed Array with Flags (O(1) amortized)
Because the number of operations is bounded, you can simulate the infinite set using a boolean array that tracks whether a number has been removed or added back. Maintain a pointer pointing to the current smallest candidate. When popSmallest() runs, move the pointer forward until you find an index marked available, mark it removed, and return it. For addBack(num), simply flip the flag back to available if the number was previously removed. Each element is scanned at most once during pointer advancement, giving O(1) amortized time for popSmallest() and O(1) for addBack(). Space complexity is O(n) for the flag array. This approach works well when operation limits are small and avoids the overhead of heap operations or maintaining an ordered set.
Recommended for interviews: The priority queue + set approach is the one most interviewers expect. It models the infinite sequence cleanly using a pointer and handles reinserted values efficiently with heap ordering. Implementing it shows you understand heap behavior, duplicate prevention, and state management in a design-style problem. The indexed array method is simpler but depends on constraints; it demonstrates optimization awareness once the baseline design is clear.
Approach 1: Approach using Priority Queue and Set
Using Priority Queue and HashSet
In this approach, we keep track of removed numbers using a priority queue (min heap) and a set to allow efficient look-up, addition, and removal operations. The priority queue keeps the numbers in sorted order, allowing us to quickly access and remove the smallest number. The set helps us to efficiently check whether a number needs to be added back.
from heapq import heappop, heappush
class SmallestInfiniteSet:
def __init__(self):
self.current = 1
self.heap = []
self.nums_in_heap = set()
def popSmallest(self) -> int:
if self.heap:
smallest = heappop(self.heap)
self.nums_in_heap.remove(smallest)
return smallest
smallest = self.current
self.current += 1
return smallest
def addBack(self, num: int) -> None:
if num < self.current and num not in self.nums_in_heap:
heappush(self.heap, num)
self.nums_in_heap.add(num)import java.util.PriorityQueue;
import java.util.Set;
import java.util.HashSet;
class SmallestInfiniteSet {
private PriorityQueue<Integer> pq;
private Set<Integer> added;
private int current;
public SmallestInfiniteSet() {
pq = new PriorityQueue<>();
added = new HashSet<>();
current = 1;
}
public int popSmallest() {
if (!pq.isEmpty()) {
int smallest = pq.poll();
added.remove(smallest);
return smallest;
}
return current++;
}
public void addBack(int num) {
if (num < current && added.add(num)) {
pq.offer(num);
}
}
}#include <set>
using namespace std;
class SmallestInfiniteSet {
set<int> nums;
int currentSmallest = 1;
public:
SmallestInfiniteSet() {
for (int i = 1; i <= 1000; ++i) {
nums.insert(i);
}
}
int popSmallest() {
int smallest = *nums.begin();
nums.erase(nums.begin());
nums.insert(currentSmallest + 1000);
currentSmallest++;
return smallest;
}
void addBack(int num) {
if (num < currentSmallest)
nums.insert(num);
}
};class SmallestInfiniteSet {
constructor() {
this.current = 1;
this.set = new Set();
}
popSmallest() {
if (this.set.size > 0) {
const min = Math.min(...this.set);
this.set.delete(min);
return min;
}
return this.current++;
}
addBack(num) {
if (num < this.current && !this.set.has(num))
this.set.add(num);
}
}/* Please note that C doesn't have a built-in priority queue. Below is a custom implementation using a min-heap, which might not be directly executable without additional heap functions. */
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_SIZE 1000
typedef struct {
int* heap;
int size;
bool present[MAX_SIZE + 1];
} SmallestInfiniteSet;
SmallestInfiniteSet* smallestInfiniteSetCreate() {
SmallestInfiniteSet* obj = malloc(sizeof(SmallestInfiniteSet));
obj->heap = malloc(sizeof(int) * (MAX_SIZE + 1));
obj->size = 0;
for (int i = 0; i <= MAX_SIZE; ++i) {
obj->present[i] = true;
}
return obj;
}
void heapify(int* heap, int size, int index) {
int smallest = index;
int left = 2 * index + 1;
int right = 2 * index + 2;
if (left < size && heap[left] < heap[smallest])
smallest = left;
if (right < size && heap[right] < heap[smallest])
smallest = right;
if (smallest != index) {
int temp = heap[index];
heap[index] = heap[smallest];
heap[smallest] = temp;
heapify(heap, size, smallest);
}
}
void insertHeap(SmallestInfiniteSet* obj, int num) {
int i;
i = obj->size++;
obj->heap[i] = num;
while (i && obj->heap[i] < obj->heap[(i - 1) / 2]) {
int temp = obj->heap[i];
obj->heap[i] = obj->heap[(i - 1) / 2];
obj->heap[(i - 1) / 2] = temp;
i = (i - 1) / 2;
}
}
int removeMin(SmallestInfiniteSet* obj) {
if (obj->size <= 0)
return INT_MAX;
if (obj->size == 1) {
obj->size--;
return obj->heap[0];
}
int root = obj->heap[0];
obj->heap[0] = obj->heap[--obj->size];
heapify(obj->heap, obj->size, 0);
return root;
}
int smallestInfiniteSetPopSmallest(SmallestInfiniteSet* obj) {
int smallest = removeMin(obj);
obj->present[smallest] = false;
return smallest;
}
void smallestInfiniteSetAddBack(SmallestInfiniteSet* obj, int num) {
if (!obj->present[num]) {
insertHeap(obj, num);
obj->present[num] = true;
}
}
void smallestInfiniteSetFree(SmallestInfiniteSet* obj) {
free(obj->heap);
free(obj);
}using System;
using System.Collections.Generic;
public class SmallestInfiniteSet {
private SortedSet<int> set;
private int current;
public SmallestInfiniteSet() {
set = new SortedSet<int>();
current = 1;
}
public int PopSmallest() {
if (set.Count > 0) {
int smallest = set.Min;
set.Remove(smallest);
return smallest;
}
return current++;
}
public void AddBack(int num) {
if (num < current && !set.Contains(num))
set.Add(num);
}
}Explanation
Python's solution leverages 'heapq' as a priority queue with an auxiliary set to track numbers.
- Priority queue 'heap' stores numbers to maintain an ordered sequence of smallest values.
- Set 'nums_in_heap' ensures that there are no duplicate entries in the heap.
- The 'current' maintains the next smallest number when the heap is empty.
Time Complexity: O(log n) for both popSmallest() and addBack() operations.
Space Complexity: O(n), space required for the elements in the heap and set.
Approach 2: Approach using Indexed Array and Flags
Using Indexed Array and Flags
In this methodology, we use an integer array to monitor the appearance of each number and a boolean flag array to verify if a number is within the set. This leverages more direct array manipulations without complex data structures.
class SmallestInfiniteSet:
def __init__(self):
self.available = [True] * 1001
self.smallest = 1
def popSmallest(self) -> int:
# Find the next available smallest number
while not self.available[self.smallest]:
self.smallest += 1
# Mark the number as unavailable
self.available[self.smallest] = False
# Return the smallest number
return self.smallest
def addBack(self, num: int) -> None:
# Mark number as available
if not self.available[num]:
self.smallest = min(self.smallest, num)
self.available[num] = Truepublic class SmallestInfiniteSet {
private boolean[] available;
private int currentSmallest;
public SmallestInfiniteSet() {
available = new boolean[1001];
for (int i = 1; i <= 1000; i++) {
available[i] = true;
}
currentSmallest = 1;
}
public int popSmallest() {
while (!available[currentSmallest]) {
currentSmallest++;
}
available[currentSmallest] = false;
return currentSmallest++;
}
public void addBack(int num) {
if (!available[num] && num < currentSmallest) {
currentSmallest = num;
}
available[num] = true;
}
}#include <vector>
#include <set>
using namespace std;
class SmallestInfiniteSet {
vector<bool> available;
int currentSmallest;
public:
SmallestInfiniteSet() : available(1001, true), currentSmallest(1) {}
int popSmallest() {
while (!available[currentSmallest]) currentSmallest++;
available[currentSmallest] = false;
return currentSmallest;
}
void addBack(int num) {
if (!available[num] && num < currentSmallest) currentSmallest = num;
available[num] = true;
}
};class SmallestInfiniteSet {
constructor() {
this.available = new Array(1001).fill(true);
this.current = 1;
}
popSmallest() {
while (!this.available[this.current]) {
this.current++;
}
this.available[this.current] = false;
return this.current;
}
addBack(num) {
if (num < this.current) {
this.current = num;
}
this.available[num] = true;
}
}/* Directly manipulate with indexed arrays and flags */
#include <stdbool.h>
#include <stdlib.h>
#define MAX 1000
typedef struct {
bool available[MAX + 1];
int smallest;
} SmallestInfiniteSet;
SmallestInfiniteSet* smallestInfiniteSetCreate() {
SmallestInfiniteSet* obj = (SmallestInfiniteSet*)malloc(sizeof(SmallestInfiniteSet));
for (int i = 1; i <= MAX; i++) {
obj->available[i] = true;
}
obj->smallest = 1;
return obj;
}
int smallestInfiniteSetPopSmallest(SmallestInfiniteSet* obj) {
while (!obj->available[obj->smallest]) {
obj->smallest++;
}
int val = obj->smallest;
obj->available[val] = false;
return val;
}
void smallestInfiniteSetAddBack(SmallestInfiniteSet* obj, int num) {
if (!obj->available[num] && num < obj->smallest) {
obj->smallest = num;
}
obj->available[num] = true;
}
void smallestInfiniteSetFree(SmallestInfiniteSet* obj) {
free(obj);
}public class SmallestInfiniteSet {
private bool[] available;
private int currentSmallest;
public SmallestInfiniteSet() {
available = new bool[1001];
for (int i = 1; i <= 1000; i++) {
available[i] = true;
}
currentSmallest = 1;
}
public int PopSmallest() {
while (!available[currentSmallest]) {
currentSmallest++;
}
available[currentSmallest] = false;
return currentSmallest++;
}
public void AddBack(int num) {
if (!available[num] && num < currentSmallest) {
currentSmallest = num;
}
available[num] = true;
}
}Explanation
Python leverages a direct boolean array approach to manage state:
- Sequential checks ensure smallest viable integer remains accessible and marked.
- Array enhancements quickly adapt to additions through index manipulations.
Time Complexity: O(1), manipulated using array indexing.
Space Complexity: O(n), efficient as dictating fixed allocation of 1000+1 slots.
Approach 3: Ordered Set + Simulation
We note that the range of elements in the set given by the problem is [1, 1000], and the operations we need to support are:
popSmallest: Pop the smallest element from the setaddBack: Add an element back to the set
Therefore, we can use an ordered set to simulate this. Let's denote the ordered set as s, and the elements in the set as s_1, s_2, cdots, s_n, where n is the number of elements in the ordered set. In this problem, n \le 1000.
During initialization, we add all elements in [1, 1000] to the ordered set. The time complexity is O(n times log n).
In the popSmallest operation, we just need to pop the first element from the ordered set. The time complexity for a single operation is O(log n).
In the addBack operation, we just need to add the element back to the ordered set. The time complexity for a single operation is O(log n).
The space complexity is O(n).
class SmallestInfiniteSet {
private pq = new MinPriorityQueue<number>();
private s = new Set<number>();
constructor() {
for (let i = 1; i <= 1000; i++) {
this.pq.enqueue(i);
this.s.add(i);
}
}
popSmallest(): number {
const x = this.pq.dequeue();
this.s.delete(x);
return x;
}
addBack(num: number): void {
if (!this.s.has(num)) {
this.pq.enqueue(num);
this.s.add(num);
}
}
}
/**
* Your SmallestInfiniteSet object will be instantiated and called as such:
* var obj = new SmallestInfiniteSet()
* var param_1 = obj.popSmallest()
* obj.addBack(num)
*/type SmallestInfiniteSet struct {
s *treemap.Map
}
func Constructor() SmallestInfiniteSet {
s := treemap.NewWithIntComparator()
for i := 1; i <= 1000; i++ {
s.Put(i, nil)
}
return SmallestInfiniteSet{s}
}
func (this *SmallestInfiniteSet) PopSmallest() int {
x, _ := this.s.Min()
this.s.Remove(x.(int))
return x.(int)
}
func (this *SmallestInfiniteSet) AddBack(num int) {
this.s.Put(num, nil)
}
/**
* Your SmallestInfiniteSet object will be instantiated and called as such:
* obj := Constructor();
* param_1 := obj.PopSmallest();
* obj.AddBack(num);
*/use std::collections::BTreeSet;
struct SmallestInfiniteSet {
s: BTreeSet<i32>,
}
impl SmallestInfiniteSet {
fn new() -> Self {
let mut set = BTreeSet::new();
for i in 1..=1000 {
set.insert(i);
}
SmallestInfiniteSet { s: set }
}
fn pop_smallest(&mut self) -> i32 {
let x = *self.s.iter().next().unwrap();
self.s.remove(&x);
x
}
fn add_back(&mut self, num: i32) {
self.s.insert(num);
}
}Complexity comparison
| Approach | Complexity |
|---|---|
| Approach using Priority Queue and Set | Time Complexity: O(log n) for both popSmallest() and addBack() operations. Space Complexity: O(n), space required for the elements in the heap and set. |
| Approach using Indexed Array and Flags | Time Complexity: O(1), manipulated using array indexing. Space Complexity: O(n), efficient as dictating fixed allocation of 1000+1 slots. |
| Ordered Set + Simulation | – |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Priority Queue + Hash Set | O(log n) per operation | O(n) | General solution for design interviews where numbers can be reinserted dynamically |
| Indexed Array with Flags | O(1) amortized | O(n) | When operation limits are small and a bounded simulation of the infinite set is acceptable |
Frequently asked questions
Is Smallest Number in Infinite Set easy or hard?
Smallest Number in Infinite Set is classified as a Medium problem. The difficulty comes from designing a data structure that efficiently supports two operations while preserving ordering. Understanding heaps and state tracking makes the implementation straightforward.
Smallest Number in Infinite Set Python/Java solution
In Python, the solution typically uses heapq for the priority queue and a set for membership tracking. In Java, developers use PriorityQueue with a HashSet. Both implementations maintain a pointer for the next unused integer and achieve O(log n) operations.
How to solve Smallest Number in Infinite Set in O(n)?
An indexed array with boolean flags can simulate the infinite set when operation limits are small. Maintain a pointer to the smallest available index and move it forward when numbers are popped. addBack simply marks the index available again. Each index is processed at most once, giving O(1) amortized operations with O(n) space.
What is the best approach for Smallest Number in Infinite Set?
The most reliable approach uses a min-heap (priority queue) combined with a hash set. A pointer tracks the smallest number never removed from the infinite sequence, while the heap stores numbers added back. popSmallest runs in O(log n) when the heap is used, and addBack also costs O(log n). This design cleanly handles dynamic reinsertion.
Is Smallest Number in Infinite Set asked at Google/Amazon/Meta?
Design-style data structure problems like this frequently appear in interviews at companies such as Amazon, Google, and Meta. The problem tests heap usage, state management, and handling of dynamic insertions, which are common interview themes.
What data structure is used in Smallest Number in Infinite Set?
The optimal solution combines a min-heap (priority queue) with a hash set. The heap keeps reinserted numbers ordered so the smallest can be retrieved quickly, while the hash set prevents duplicates and supports O(1) membership checks.
What is the time complexity of Smallest Number in Infinite Set?
Using the priority queue + hash set design, both popSmallest and addBack run in O(log n) time due to heap operations. Hash set lookups are O(1). Space complexity is O(n) for the stored numbers that were removed and later reinserted.
