Smallest Number Infinite Set

// https://leetcode.com/problems/smallest-number-in-infinite-set

/*
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 integer num back into the infinite set, if it is not already in the infinite set.

Ex1:

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.

*/

// currmin = 3

// 1, 2, 3, 4, 5, 6, 7, 8
// backMap: 3, 5

#include <set>
#include <queue>
#include <unordered_set>
#include <cassert>
#include <iostream>

using namespace std;

// Priority queue on addBack operation
// Use a min heap to keep track of potential smallest number candidates.
// 
// n: number of addBack, m: number of popSmallest
// Time complexity: O((m+n)*log(n)), Space complexity: O(n)
class SmallestInfiniteSetAddBackWithPQ {
public:
    SmallestInfiniteSetAddBackWithPQ() {
    }

    int popSmallest() {
        if (pq.empty()) {
            int res = currMin;
            currMin++;
            return res;
        }

        int res = pq.top();
        pq.pop();
        // Don't forget to remove the number from the set.
        exist.erase(res);
        return res;
    }

    void addBack(int num) {
        // If the number is already in the set, we don't need to do anything.
        // If the number is greater than the current min, meaning we haven't popped it yet.
        if (exist.count(num) || num >= currMin) {
            return;
        }

        pq.push(num);
        exist.insert(num);
    }

private:
    int currMin = 1;
    priority_queue<int, vector<int>, greater<int>> pq; // min-heap
    unordered_set<int> exist;
};

// Two sets: popSet and backSet.
//
// n: number of addBack, m: number of popSmallest
// Time complexity: O((m+n)*log(m+n)), Space complexity: O(m+n)
class SmallestInfiniteSet {
public:
    SmallestInfiniteSet() {
    }

    int popSmallest() {
        if (popSet.empty() && backSet.empty()) {
            popSet.insert(1);
            return 1;
        }

        if (backSet.empty()) {
            auto lastPop = popSet.end();
            lastPop--;
            int res = *lastPop+1;
            popSet.insert(res);
            return res;
        } else {
            int res = *backSet.begin();
            backSet.erase(backSet.begin());
            popSet.insert(res);
            return res;
        }
    }

    void addBack(int num) {
        if (!popSet.count(num)) {
            return;
        }

        backSet.insert(num);
        popSet.erase(num);
    }

private:
    set<int> popSet;
    set<int> backSet;
};

int main() {
    SmallestInfiniteSet s;

    s.addBack(2);
    assert(s.popSmallest() == 1);
    assert(s.popSmallest() == 2);
    assert(s.popSmallest() == 3);
    s.addBack(1);
    assert(s.popSmallest() == 1);
    assert(s.popSmallest() == 4);
    assert(s.popSmallest() == 5);

    SmallestInfiniteSetAddBackWithPQ sPQ;
    sPQ.addBack(2);
    assert(sPQ.popSmallest() == 1);
    assert(sPQ.popSmallest() == 2);
    assert(sPQ.popSmallest() == 3);
    sPQ.addBack(1);
    assert(sPQ.popSmallest() == 1);
    assert(sPQ.popSmallest() == 4);
    assert(sPQ.popSmallest() == 5);

    return 0;
}```

Last updated