Smallest Number in Infinite Set

Updated on 24 June, 2025
Smallest Number in Infinite Set header image

Problem Statement

In the given task, we are required to create a class SmallestInfiniteSet which simulates an infinite set containing all positive integers starting from 1. The class should support specific operations:

  • Initialization: When an object of this class is instantiated, it must inherently represent an infinite set starting with the smallest positive integer, 1.
  • Pop the Smallest Integer: Via the method popSmallest(), the class should be able to remove the smallest integer from the set and return it. If this operation is repeatedly called, it should continue to return and remove the next smallest integer consecutively.
  • Adding an Integer Back: The method addBack(int num) allows a number to be re-introduced into the set. However, it will only add the number back if it does not currently exist in the set, ensuring no duplicates and that the set remains ordered starting from the smallest number not present in the set.

Implementing this requires maintaining a structure that can efficiently manage infinite integers and handle dynamic additions and deletions.

Examples

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, remove it from the set
smallestInfiniteSet.popSmallest();    // return 2, remove it from the set
smallestInfiniteSet.popSmallest();    // return 3, remove it from the set
smallestInfiniteSet.addBack(1);       // add 1 back into the set
smallestInfiniteSet.popSmallest();    // return 1, remove it from the set
smallestInfiniteSet.popSmallest();    // return 4, remove it from the set
smallestInfiniteSet.popSmallest();    // return 5, remove it from the set

Constraints

  • 1 <= num <= 1000
  • At most 1000 calls will be made in total to popSmallest and addBack.

Approach and Intuition

To approach the implementation of the SmallestInfiniteSet, consider the following:

  1. Track the Next Smallest Number:

    • Maintain a variable current initialized to 1. This pointer keeps track of the smallest number not yet removed from the set.
  2. Manage Add-Backs:

    • Use a min-heap (priority queue) to manage numbers that were removed via popSmallest() but later re-added using addBack().
    • To avoid duplicates, maintain a set of the numbers currently present in the heap.
  3. popSmallest():

    • If the min-heap is non-empty, pop the smallest number from it (and remove it from the set).
    • Otherwise, return current and increment it.
  4. addBack(num):

    • Only add the number back if it's less than current and not already in the set. This prevents duplicates and respects the infinite structure’s order.

This approach ensures O(log N) time complexity for each operation and correctly simulates an infinite set with dynamic add and pop capabilities.

Solutions

  • C++
  • Java
  • JavaScript
  • Python
cpp
class InfiniteSetManager {
    unordered_set<int> presentSet;
    priority_queue<int, vector<int>, greater<int>> heap;
    int smallest;

public:
    InfiniteSetManager() {
        smallest = 1;
    }

    int getSmallest() {
        int result;
        if (!heap.empty()) {
            result = heap.top();
            presentSet.erase(result);
            heap.pop();
        } else {
            result = smallest;
            smallest += 1;
        }
        return result;
    }

    void restore(int num) {
        if (smallest <= num || presentSet.find(num) != presentSet.end()) {
            return;
        }
        heap.push(num);
        presentSet.insert(num);
    }
};

The provided C++ code defines a class InfiniteSetManager which manages an infinitely growing set of integers. The class encapsulates the functionality to retrieve the smallest number from the set and to restore numbers back into the set if needed. Here's a breakdown of its implementation and usage:

  • Data Structures Used:

    • unordered_set<int> presentSet: Keeps track of numbers that have been restored.
    • priority_queue<int, vector<int>, greater<int>> heap: A min-heap to efficiently get the smallest restored number.
  • Constructor - InfiniteSetManager()

    • Initializes smallest to 1, indicating the starting smallest number of the set.
  • Method - int getSmallest()

    • Retrieves and removes the smallest number available in the set:
      • If the heap is not empty, it indicates there are restored numbers. It pops the smallest from the heap.
      • If the heap is empty, it uses and increments the smallest counter.
    • Returns the smallest number.
  • Method - void restore(int num)

    • Adds a number back into the set, only if the number is less than the current smallest and isn't already present in the heap:
      • Checks if restoring is necessary (num is less than smallest and not in presentSet).
      • If conditions are met, adds num to both heap and presentSet for efficient access and tracking.

This implementation allows efficient management of an infinite set of integers, particularly focusing on operations that involve retrieving and restoring the smallest element. It optimizes these operations using a combination of a priority queue for direct access to the smallest number and a set for membership checking and tracking restored numbers.

java
class MinimalInfiniteSet {
    private HashSet<Integer> setExists;
    private PriorityQueue<Integer> minHeap;
    private Integer smallest;

    public MinimalInfiniteSet() {
        setExists = new HashSet<>();
        minHeap = new PriorityQueue<>();
        smallest = 1;
    }
    
    public int getSmallest() {
        int result;
        if (!minHeap.isEmpty()) {
            result = minHeap.poll();
            setExists.remove(result);
        } else {
            result = smallest;
            smallest += 1;
        }
        return result;
    }
    
    public void restore(int number) {
        if (smallest <= number || setExists.contains(number)) {
            return;
        }
        minHeap.add(number);
        setExists.add(number);
    }
}

The provided solution demonstrates how to implement a class MinimalInfiniteSet in Java for managing a set of numbers in an infinite set, such that you can continuously extract the smallest number and have the ability to restore numbers back into the set under specific conditions. Here is an overview of the code implementation and its functionality:

  • Class Variables:

    • setExists: A HashSet to keep track of numbers that are restored or interacted with.
    • minHeap: A PriorityQueue to efficiently get the smallest number available.
    • smallest: An Integer holding the current smallest number not yet included in the minHeap.
  • Constructor: MinimalInfiniteSet()

    • Initializes setExists and minHeap.
    • Sets smallest to 1.
  • Method: getSmallest()

    • Retrieves and returns the smallest number from the set.
    • If minHeap is not empty, remove the number from both the minHeap and setExists.
    • If minHeap is empty, increment smallest to keep track of new smallest numbers.
  • Method: restore(int number)

    • Restores a number into the set if it meets certain conditions:
      • The number must be less than smallest.
      • The number should not already exist in setExists.
    • Adds the number to both minHeap and setExists if conditions are met.

This setup ensures seamless management of the smallest numbers in the dynamic set and proper functioning of the number restoration system under prescribed conditions. By using a heap (PriorityQueue) and a hash set (HashSet), the solution efficiently manages insertion and deletion operations needed for this specific functionality.

js
let InfiniteNumberSet = function() {
    this.numSet = new HashSet();
    this.minPQ = new MinHeapPQ();
    this.minInt = 1;
};

InfiniteNumberSet.prototype.getSmallest = function() {
    if (!this.minPQ.isEmpty()) {
        minVal = this.minPQ.popMin();
        this.numSet.remove(minVal);
    } else {
        minVal = this.minInt;
        this.minInt += 1;
    }
    return minVal;
};

InfiniteNumberSet.prototype.restore = function(value) {
    if (this.minInt <= value || this.numSet.contains(value)) {
        return;
    }
    this.minPQ.insert(value);
    this.numSet.add(value);
};


// === MIN HEAP PRIORITY QUEUE CLASS === //
class MinHeapPQ {
    constructor() {
        this.elements = [];
    }

    getParent(i) {
        return Math.floor((i - 1) / 2);
    }
    getLeftChild(i) {
        return 2 * i + 1;
    }
    getRightChild(i) {
        return 2 * i + 2;
    }
    
    exchange(i1, i2) {
        [this.elements[i1], this.elements[i2]] = [this.elements[i2], this.elements[i1]];
    }

    top() {
        if (this.elements.length === 0) {
            throw new Error("Heap is empty");
        }
        return this.elements[0];
    }

    add(value) {
        this.elements.push(value);
        let current = this.elements.length - 1;
        let parent = this.getParent(current);
        while (current > 0 && this.elements[current] < this.elements[parent]) {
            this.exchange(current, parent);
            current = parent;
            parent = this.getParent(current);
        }
    }

    extractMin() {
        if (this.elements.length === 0) {
            throw new Error("Heap is empty");
        }
        const minimum = this.elements[0];
        const end = this.elements.pop();
        if (this.elements.length > 0) {
            this.elements[0] = end;
            let current = 0;
            let left = this.getLeftChild(current);
            let right = this.getRightChild(current);
            while (
                (left < this.elements.length && this.elements[left] < this.elements[current]) || 
                (right < this.elements.length && this.elements[right] < this.elements[current])
            ) {
                const smaller = (right >= this.elements.length || 
                    this.elements[left] < this.elements[right]) ? left : right;

                this.exchange(current, smaller);
                current = smaller;
                left = this.getLeftChild(current);
                right = this.getRightChild(current);
            }
        }
        return minimum;
    }

    count() {
        return this.elements.length;
    }
    
    isEmpty() {
        return this.elements.length === 0;
    }
}


// === HASH SET CLASS === //
class HashSet {
  constructor() {
    this.storage = {};
    this.count = 0;
  }

  add(item) {
    if (!this.contains(item)) {
      this.storage[item] = true;
      this.count++;
    }
  }

  remove(item) {
    if (this.contains(item)) {
      delete this.storage[item];
      this.count--;
    }
  }

  contains(item) {
    return this.storage.hasOwnProperty(item);
  }

  getSize() {
    return this.count;
  }

  isEmpty() {
    return this.count === 0;
  }

  clear() {
    this.storage = {};
    this.count = 0;
  }
}

The solution provided implements an InfiniteNumberSet class in JavaScript to manage an infinitely growing set of numbers, efficiently allowing retrieval and restoration of the smallest numbers.

  • InfiniteNumberSet utilizes two main internal data structures:

    • A HashSet for tracking numbers present in the set.
    • A MinHeapPQ (Minimum Heap Priority Queue) to facilitate the efficient retrieval of the smallest number.
  • The getSmallest method of InfiniteNumberSet:

    • Checks if the priority queue is not empty and if so, pops the minimum element from it.
    • If the priority queue is empty, it resorts to using an internal counter (minInt) that tracks the smallest integer not yet used or restored.
  • The restore method:

    • Takes a number and adds it back into the set and priority queue if it does not exist in the set and is less than minInt.
  • For the MinHeapPQ class:

    • Operations such as add to insert a new value and extractMin to retrieve and remove the smallest value are implemented.
    • Helper methods are used to maintain the heap structure after modifications.
  • The HashSet class:

    • Provides basic operations such as add, remove, and contains to manage its entries.
    • Utilizes an internal JavaScript object (storage) for storing elements, with methods to add or remove items and check for their presence.

By leveraging a minimum heap priority queue, this solution ensures that adding a number and getting the smallest number can be performed efficiently. In contrast, the set guarantees that each number is unique in the queue and can be quickly checked if it needs restoration. These methods combined allow the InfiniteNumberSet to handle a dynamic set of numbers effectively where both retrieval of the smallest element and restoration are optimized for performance.

python
class MinimumInfiniteSet:
    def __init__(self):
        self.tracked_set = set()
        self.heap_list = []
        self.smallest_value = 1

    def getSmallest(self) -> int:
        if len(self.heap_list):
            smallest = heapq.heappop(self.heap_list)
            self.tracked_set.remove(smallest)
        else:
            smallest = self.smallest_value
            self.smallest_value += 1
        return smallest

    def addElement(self, value: int) -> None:
        if self.smallest_value <= value or value in self.tracked_set:
            return
        heapq.heappush(self.heap_list, value)
        self.tracked_set.add(value)

The provided Python code snippet represents an implementation for managing a set of integers where you can dynamically fetch the smallest number and add new integers. The implementation uses a combination of a heap and a set to efficiently manage the elements and provide operations to add an element and get the smallest element.

  • The class MinimumInfiniteSet contains:
    • Initialization of tracked_set (a set to keep track of elements that are currently in the heap), heap_list (a heap to efficiently get the smallest element), and smallest_value (a variable to keep the smallest number not in the heap).

    • The method getSmallest() retrieves and returns the smallest available number. It maintains the integrity of the underlying data structure, updates the smallest_value if the heap is exhausted, and ensures that the set and the heap remain consistent by popping the smallest element from the heap and removing it from the set.

    • The method addElement(value: int) is designed to add elements to the set efficiently. It adds an element to the heap and the set only if it is less than smallest_value and not already tracked in the set. This method utilizes the heap to ensure that the minimum value can always be efficiently retrieved.

This code is effective for scenarios where there is a need to frequently access the smallest element of a set and update the set concurrently without re-computing the entire data structure. The use of both a set and a heap ensures that both the operations of adding an element and retrieving the minimum are handled efficiently.

Comments

No comments yet.