The Wayback Machine - https://web.archive.org/web/20210125131336/https://github.com/grandyang/leetcode/issues/381
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LeetCode] 381. Insert Delete GetRandom O(1) - Duplicates allowed #381

Open
grandyang opened this issue May 30, 2019 · 2 comments
Open

[LeetCode] 381. Insert Delete GetRandom O(1) - Duplicates allowed #381

grandyang opened this issue May 30, 2019 · 2 comments

Comments

@grandyang
Copy link
Owner

@grandyang grandyang commented May 30, 2019

 

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函数是这题的难点,我们首先看哈希表中有没有val,没有的话直接返回false。然后我们取出nums的尾元素,把尾元素哈希表中的位置数组中的最后一个位置更新为m[val]的尾元素,这样我们就可以删掉m[val]的尾元素了,如果m[val]只有一个元素,那么我们把这个映射直接删除。然后我们将nums数组中的尾元素删除,并把尾元素赋给val所在的位置,注意我们在建立哈希表的映射的时候需要用堆而不是普通的vector数组,因为我们每次remove操作后都会移除nums数组的尾元素,如果我们用vector来保存数字的坐标,而且只移出末尾数字的话,有可能出现前面的坐标大小超过了此时nums的大小的情况,就会出错,所以我们用优先队列对所有的相同数字的坐标进行自动排序,每次把最大位置的坐标移出即可,参见代码如下:

 

解法一:

class RandomizedCollection {
public:
    /** Initialize your data structure here. */
    RandomizedCollection() {}
    
    /** Inserts a value to the collection. Returns true if the collection did not already contain the specified element. */
    bool insert(int val) {
        m[val].push(nums.size());
        nums.push_back(val);
        return m[val].size() == 1;
    }
    
    /** Removes a value from the collection. Returns true if the collection contained the specified element. */
    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;
    }
    
    /** Get a random element from the collection. */
    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:
    /** Initialize your data structure here. */
    RandomizedCollection() {}
    
    /** Inserts a value to the collection. Returns true if the collection did not already contain the specified element. */
    bool insert(int val) {
        m[val].insert(nums.size());
        nums.push_back(val);
        return m[val].size() == 1;
    }
    
    /** Removes a value from the collection. Returns true if the collection contained the specified element. */
    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;
    }
    
    /** Get a random element from the collection. */
    int getRandom() {
        return nums[rand() % nums.size()];
    }

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

 

类似题目:

Insert Delete GetRandom O(1)

 

参考资料:

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

https://discuss.leetcode.com/topic/53659/c-two-solutions

https://discuss.leetcode.com/topic/54381/c-128m-solution-real-o-1-solution

 

LeetCode All in One 题目讲解汇总(持续更新中...)

@lld2006
Copy link

@lld2006 lld2006 commented Jul 14, 2019

@lld2006
Copy link

@lld2006 lld2006 commented Jun 18, 2020

最好用unordered_map<int, vector<int> >来代替unordered_set, 可以节约空间, 但是
nums就要改成vector<pair<int,int>>, 第二个int记录 这个值在map对应的vector中的index。

class RandomizedCollection {
public:
/** Initialize your data structure here. */
RandomizedCollection() {

}

/** Inserts a value to the collection. Returns true if the collection did not already contain the specified element. */
bool insert(int val) {
    hmap[val].push_back(values.size());    
    values.emplace_back(val, hmap[val].size()-1);
    return hmap[val].size()==1;
}

/** Removes a value from the collection. Returns true if the collection contained the specified element. */
bool remove(int val) {
    if(!hmap.count(val)) return false;
    int index = hmap[val].back();
    hmap[val].pop_back();
    auto const&last_entry = values.back();
    hmap[last_entry.first][last_entry.second] = index;
    values[index]=last_entry;
    if(hmap[val].empty())hmap.erase(val);
    return true;
}

/** Get a random element from the collection. */
int getRandom() {
    // write your code here
    return values[rand()%values.size()].first;
}
vector<pair<int,int>> values;
unordered_map<int,vector<int>> hmap;

};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants