Problem Description
Given an integer n and a list of unique integers called blacklist, design an algorithm to pick a random integer from the range [0, n - 1] that is not in the blacklist. Every valid integer (i.e. not blacklisted) should have an equal probability of being chosen. The goal is to optimize the solution to minimize calls to the built-in random function.
Key Insights
- The valid output range has a total count of available numbers W = n - len(blacklist).
- Instead of repeatedly generating random numbers and checking against a set, we can transform the range into a contiguous block [0, W - 1] and map any blacklist number within that block to a valid number in the upper range.
- Mapping is precomputed: for any blacklisted number x in [0, W-1], assign it a valid number from [W, n-1] that is not blacklisted.
- This approach ensures O(1) pick time after an O(b) precomputation where b = length of blacklist.
Space and Time Complexity
Time Complexity: constructor - O(b) for initializing data structures, pick() - O(1) per call.
Space Complexity: O(b) for storing the blacklist set and mapping.
Solution
We compute the count of valid numbers, W = n - len(blacklist). For numbers in the blacklist that are less than W, we need to map them to a valid number from the range [W, n-1]. To do this, we first store all blacklist numbers in the higher range [W, n-1] in a set for quick lookup. Then for each blacklisted value in [0, W-1], we iterate from W upwards to find a number that is not blacklisted and assign it in a mapping. During the pick() function, we generate a random integer r in [0, W-1]. If r is in our mapping, we return its corresponding mapped value; otherwise, we return r as it is already a valid number.