[LeetCode] 381. Insert Delete GetRandom O(1) - Duplicates allowed 常數時間內插入刪除和獲得隨機數 - 允許重復


 

Design a data structure that supports all following operations in average O(1) time.

Note: Duplicate elements are allowed.

 

  1. insert(val): Inserts an item val to the collection.
  2. remove(val): Removes an item val from the collection if present.
  3. getRandom: Returns a random element from current collection of elements. The probability of each element being returned is linearly related to the number of same value the collection contains.

 

Example:

// Init an empty collection.
RandomizedCollection collection = new RandomizedCollection();

// Inserts 1 to the collection. Returns true as the collection did not contain 1.
collection.insert(1);

// Inserts another 1 to the collection. Returns false as the collection contained 1. Collection now contains [1,1].
collection.insert(1);

// Inserts 2 to the collection, returns true. Collection now contains [1,1,2].
collection.insert(2);

// getRandom should return 1 with the probability 2/3, and returns 2 with the probability 1/3.
collection.getRandom();

// Removes 1 from the collection, returns true. Collection now contains [1,2].
collection.remove(1);

// getRandom should return 1 and 2 both equally likely.
collection.getRandom();

 

這題是之前那道 Insert Delete GetRandom O(1) 的拓展,與其不同的是,之前那道題不能有重復數字,而這道題可以有,那么就不能像之前那道題那樣建立每個數字和其坐標的映射了,但是我們可以建立數字和其所有出現位置的集合之間的映射,雖然寫法略有不同,但是思路和之前那題完全一樣,都是將數組最后一個位置的元素和要刪除的元素交換位置,然后刪掉最后一個位置上的元素。對於 insert 函數,我們將要插入的數字在 nums 中的位置加入 m[val] 數組的末尾,然后在數組 nums 末尾加入 val,我們判斷是否有重復只要看 m[val] 數組只有剛加的 val 一個值還是有多個值。remove 函數是這題的難點,我們首先看 HashMap 中有沒有 val,沒有的話直接返回 false。然后我們取出 nums 的尾元素,把尾元素 HashMap 中的位置數組中的最后一個位置更新為 m[val] 的尾元素,這樣我們就可以刪掉 m[val] 的尾元素了,如果 m[val] 只有一個元素,那么我們把這個映射直接刪除。然后將 nums 數組中的尾元素刪除,並把尾元素賦給 val 所在的位置,注意我們在建立 HashMap 的映射的時候需要用堆而不是普通的 vector 數組,因為我們每次 remove 操作后都會移除 nums 數組的尾元素,如果我們用 vector 來保存數字的坐標,而且只移出末尾數字的話,有可能出現前面的坐標大小超過了此時 nums 的大小的情況,就會出錯,所以我們用優先隊列對所有的相同數字的坐標進行自動排序,每次把最大位置的坐標移出即可,參見代碼如下:

 

解法一:

class RandomizedCollection {
public:
    RandomizedCollection() {}
    bool insert(int val) {
        m[val].push(nums.size());
        nums.push_back(val);
        return m[val].size() == 1;
    }
    bool remove(int val) {
        if (m[val].empty()) return false;
        int idx = m[val].top();
        m[val].pop();
        if (nums.size() - 1 != idx) {
            int t = nums.back();
            nums[idx] = t;
            m[t].pop();
            m[t].push(idx);
        }
        nums.pop_back();
        return true;
    }
    int getRandom() {
        return nums[rand() % nums.size()];
    }
private:
    vector<int> nums;
    unordered_map<int, priority_queue<int>> m;
};

 

有網友指出上面的方法其實不是真正的 O(1) 時間復雜度,因為優先隊列的 push 不是常數級的,博主一看果然是這樣的,為了嚴格的遵守 O(1) 的時間復雜度,我們將優先隊列換成 unordered_set,其插入刪除的操作都是常數量級的,其他部分基本不用變,參見代碼如下:

 

解法二:

class RandomizedCollection {
public:
    RandomizedCollection() {}
    bool insert(int val) {
        m[val].insert(nums.size());
        nums.push_back(val);
        return m[val].size() == 1;
    }
    bool remove(int val) {
        if (m[val].empty()) return false;
        int idx = *m[val].begin();
        m[val].erase(idx);
        if (nums.size() - 1 != idx) {
            int t = nums.back();
            nums[idx] = t;
            m[t].erase(nums.size() - 1);
            m[t].insert(idx);
        } 
        nums.pop_back();
        return true;
    }
    int getRandom() {
        return nums[rand() % nums.size()];
    }

private:
    vector<int> nums;
    unordered_map<int, unordered_set<int>> m;
};

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/381

 

類似題目:

Insert Delete GetRandom O(1)

 

參考資料:

https://leetcode.com/problems/insert-delete-getrandom-o1-duplicates-allowed/

https://leetcode.com/problems/insert-delete-getrandom-o1-duplicates-allowed/discuss/85635/c-two-solutions

https://leetcode.com/problems/insert-delete-getrandom-o1-duplicates-allowed/discuss/85541/C%2B%2B-128m-Solution-Real-O(1)-Solution

https://leetcode.com/problems/insert-delete-getrandom-o1-duplicates-allowed/discuss/197641/C%2B%2B-30-ms-hashmap-hashset-and-vector

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM