381. Insert Delete GetRandom O(1) - Duplicates allowed

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.

  • insert(val): Inserts an item val to the collection.
  • remove(val): Removes an item val from the collection if present.
  • 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();

就是弄一个数据结构,使得其可以插入数据、删除数据、返回随机数据。

但是它的时间复杂度只能是O(1)

代码实现

这里我先实现了一种使用map和一个整数记录了其大小。

class RandomizedCollection {
    map<int, int> hash;
    int size;
public:
    RandomizedCollection() {
        size = 0;
    }

    bool insert(int val) {
        hash[val]++;
        size++;
        return (hash[val] == 1);
    }

    bool remove(int val) {
        if(hash.count(val) == 0) return false;
        if(--hash[val] == 0) hash.erase(val);
        size--;
        return true;
    }

    int getRandom() {
        int ind = rand() % size + 1;
        map<int, int>::iterator it; 
        for(it = hash.begin(); it != hash.end(); it++) {
            if(ind <= it->second) return it->first;
            else ind -= it->second;
        }
        return it->first;
    }
};

/**
 * Your RandomizedCollection object will be instantiated and called as such:
 * RandomizedCollection obj = new RandomizedCollection();
 * bool param_1 = obj.insert(val);
 * bool param_2 = obj.remove(val);
 * int param_3 = obj.getRandom();
 */

但是这种方法在返回随机数的时候复杂度是O(n),所以这里需要做一些修改。

这里有一种更好的方法,就是使用vector和map进行。使用vector方便搜索,使用map比较容易进行存储索引及值的映射。插入和搜索比较简单,问题是删除会巧妙一点。这里的话,使用的是删除某个元素,那么就把该元素的映射的索引都找到,找到索引最大的那个,把它和vector存储的最外面的元素做一个交换,那么就只需要修改最外面元素的索引,就能把需要的数字删除。

class RandomizedCollection {
    map<int, vector<int> > hash;
    vector<int> res;
public:
    RandomizedCollection() {

    }

    bool insert(int val) {
        hash[val].push_back(res.size());
        res.push_back(val);
        return (hash[val].size() == 1);
    }

    bool remove(int val) {
        if(!hash.count(val)) return false;
        int tp = hash[val].back();
        hash[val].pop_back();
        if(!hash[val].size()) hash.erase(val);
        if(res.size() != tp + 1) {
            res[tp] = res[res.size()-1];
            hash[res[tp]].pop_back();
            hash[res[tp]].insert(hash[res[tp]].begin(), tp);
        }
        res.pop_back();
        return true;
    }

    int getRandom() {
        int sz = res.size();
        if(!sz) return 1;
        int ind = rand() % sz;
        return res[ind];
    }
};

/**
 * Your RandomizedCollection object will be instantiated and called as such:
 * RandomizedCollection obj = new RandomizedCollection();
 * bool param_1 = obj.insert(val);
 * bool param_2 = obj.remove(val);
 * int param_3 = obj.getRandom();
 */

在leetcode1我看到了有人使用multimap,算法效率和上面我写的一样:

class RandomizedCollection {
private:
    std::vector<int> nums;
    std::unordered_multimap<int, int> pcmap; 
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) {
        nums.push_back(val);
        int count = pcmap.count(val);
        pcmap.insert(std::make_pair(val, nums.size() - 1));
        return count == 0;
    }

    /** Removes a value from the collection. Returns true if the collection contained the specified element. */
    bool remove(int val) {
        auto iter = pcmap.find(val);
        if (iter == pcmap.end())
            return false;

        int pos = iter->second;
        pcmap.erase(iter, std::next(iter));

        int lastVal = nums.back();
        nums[pos] = lastVal;

        // Special case in consideration: remove the last inserted value (count: 0->1)
        for (auto iterLast = pcmap.find(lastVal); iterLast != pcmap.end(); iterLast++) {
            if (iterLast->second == nums.size() - 1) {
                pcmap.erase(iterLast, std::next(iterLast));
                pcmap.insert(std::make_pair(lastVal, pos));
                break; // break early to avoid meaningless loops
            }  
        } 

        nums.pop_back();  // pop last 

        return true;
    }

    /** Get a random element from the collection. */
    int getRandom() {
        int pos = rand() % nums.size();
        return nums[pos];
    }
};
/**
 * Your RandomizedCollection object will be instantiated and called as such:
 * RandomizedCollection obj = new RandomizedCollection();
 * bool param_1 = obj.insert(val);
 * bool param_2 = obj.remove(val);
 * int param_3 = obj.getRandom();
 */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值