LeetCode 398. Random Pick Index [Pool Sampling/Hash Table/Randomization] Medium

This article is part of the Conquest LeetCode series, which began officially in 2021/08/12.Because some of the titles on LeetCode are locked, this series will last at least until the date all unlocked titles are brushed.Since LeetCode is constantly creating new questions, the end date of this series may be forever.In this series of brushing articles, I will not only explain a variety of problem solving ideas and their optimization, but also use a variety of programming languages to achieve the problem, when it comes to general solutions, I will summarize the corresponding algorithm templates.

To facilitate running debugging on PC s and sharing code files, I also set up a warehouse: https://github.com/memcpy0/LeetCode-Conquest .In this warehouse, you can not only see the LeetCode topic link, the title code, the title article link, similar topic summary, general solution summary, etc., but also see the frequency of the original topic and related important information such as enterprises.If you have other preferred solutions, you can share them with others.

As the content of this series of articles may change at any time, you are welcome to follow and collect them Conquer LeetCode Series Articles Directory One article for memo.

Given an integer array nums with possible duplicates, randomly output the index of a given target number. You can assume that the given target number must exist in the array.

Implement the Solution class:

  • Solution(int[] nums) Initializes the object with the array nums.
  • int pick(int target) Picks a random index i from nums where nums[i] == target. If there are multiple valid i's, then each index should have an equal probability of returning.

Example 1:

Input
["Solution", "pick", "pick", "pick"]
[[[1, 2, 3, 3, 3]], [3], [1], [3]]
Output
[null, 4, 0, 2]

Explanation
Solution solution = new Solution([1, 2, 3, 3, 3]);
solution.pick(3); // It should return either index 2, 3, or 4 randomly. Each index should have equal probability of returning.
solution.pick(1); // It should return 0. Since in the array only nums[0] is equal to 1.
solution.pick(3); // It should return either index 2, 3, or 4 randomly. Each index should have equal probability of returning.

Constraints:

  • 1 <= nums.length <= 2 * 104
  • -231 <= nums[i] <= 231 - 1
  • target is an integer from nums.
  • At most 104 calls will be made to pick.

Title: Given an integer array that may contain duplicate elements, the index of a given number is required to be output randomly.You can assume that a given number must exist in the array.Note that the array size can be very large.Solutions that use too much extra space will not pass the test.

Solution 1 Hash Table+Randomization

A hash table is used to store the set of subscripts corresponding to each value and value, and a random subscript is chosen to return when looking for a target.At this point, the time complexity of the Solution class construction method is O ( n ) O(n) O(n), the time complexity of the pick method is O ( 1 ) O(1) O(1), spatial complexity is O ( n ) O(n) O(n) .

//C++ version
unsigned seed = chrono::system_clock::now().time_since_epoch().count();
default_random_engine generator(seed);
class Solution {
private:
    unordered_map<int, vector<int>> rec;
    uniform_int_distribution<int> ud;
public:
    Solution(vector<int>& nums) {
        for (int i = 0, n = nums.size(); i < n; ++i)
            rec[nums[i]].push_back(i);
    }
    int pick(int target) {
        const vector<int>& v = rec[target];
        int n = v.size() - 1;
        ud.param(uniform_int_distribution<>::param_type{0, n});
        return v[ud(generator)];
    }
};
//Execution time: 116 ms, beating 25.22% of all C++ submissions
//Memory consumption: 50.5 MB, beating 22.75% of all C++ submissions
//Java version
class Solution {
    private HashMap<Integer, ArrayList<Integer>> rec;
    public Solution(int[] nums) {
        rec = new HashMap<>();
        for (int i = 0; i < nums.length; ++i) {
            if (!rec.containsKey(nums[i])) rec.put(nums[i], new ArrayList<>());
            rec.get(nums[i]).add(i);
        }
    }
    
    public int pick(int target) {
        int n = rec.get(target).size();
        int k = new Random().nextInt(n);
        return rec.get(target).get(k);
    }
}
//Execution time: 87 ms, beating 10.81% of all Java submissions
//Memory consumption: 51.8 MB, beating 5.00% of all Java submissions

Solution 2 Tank Sampling

This is correct, but it wastes a lot of extra space and leads to unnecessary time consumption.We can use Storage Tank Sampling Random Reservoir Sampling .Save the entire nums array and select the target's subscript value each time with equal probability:

//C++ version
unsigned seed = chrono::system_clock::now().time_since_epoch().count();
default_random_engine generator(seed);
class Solution {
private:
    vector<int> arr;
    uniform_int_distribution<int> ud;
public:
    Solution(vector<int>& nums) { arr = nums; }
    int pick(int target) { 
        //Scan the entire array, and if you encounter the cnt's target, select its subscript with a probability of 1/cnt and replace the previously selected subscript
        int cnt = 0, ans;
        for (int i = 0, n = arr.size(); i < n; ++i) {
            if (arr[i] == target) {
                ++cnt;
                ud.param(uniform_int_distribution<>::param_type{1, cnt});
                if (ud(generator) == cnt) ans = i;
            }
        }
        return ans;
    }
};
//Execution time: 48 ms, beat 96.43% of all C++ submissions
//Memory consumption: 34.7 MB, beating 69.78% of all C++ submissions

By saving only the nums start and end iterators, you can reduce the extra space to O ( 1 ) O(1) O(1), the rest of the ideas are identical:

//C++ version
unsigned seed = chrono::system_clock::now().time_since_epoch().count();
default_random_engine generator(seed);
class Solution {
private:
    vector<int>::iterator begin, end;
    uniform_int_distribution<int> ud;
public:
    Solution(vector<int>& nums) { 
        begin = nums.begin(); 
        end = nums.end(); 
    }
    int pick(int target) {
        int cnt = 0, ans, i = 0;
        for (auto cur = begin; cur != end; ++cur, ++i) {
            if (*cur == target) {
                ++cnt;
                ud.param(uniform_int_distribution<>::param_type{1, cnt});
                if (ud(generator) == cnt) ans = i;
            }
        }
        return ans;
    }
}; 
//Execution time: 48 ms, beat 96.43% of all C++ submissions
//Memory consumption: 33.9 MB, beating 85.74% of all C++ submissions

Tags: Algorithm leetcode

Posted on Sat, 04 Sep 2021 01:34:17 -0400 by JDcrack