leetcode:381. O(1) 时间插入、删除和获取随机元素 - 允许重复

题目来源

题目描述

在这里插入图片描述

class RandomizedCollection {
public:
    RandomizedCollection() {

    }
    
    bool insert(int val) {

    }
    
    bool remove(int val) {

    }
    
    int getRandom() {

    }
};

题目解析

思路

leetcode:380. O(1) 时间插入、删除和获取随机元素 Insert Delete GetRandom O(1)不同的是,380不运行有重复数字,而本题有重复数字。怎么处理呢?思路类似,同样是map+数组,区别在于map中

  • 380题:建立每个数字和其坐标的映射
  • 本题:建立每个数字和其出现的所有位置的集合之间的映射

那么:

  • 对于insert函数:
    • 将val压入num的末尾
    • 将val的索引压入map[val]的末尾
    • 因为题目要求返回true表示第一次插入,返回false表示不是第一次插入,因此我们直接判断m[val] == 1
  • 对于remove函数:
    • 先看map中有没有val,没有就直接返回
    • 如果有,
      • 那么需要删除的元素的索引为:deleteIndex = m[val].back()
      • 然后取出待交换的元素: swapVal = vec.back(); swapIndex = m[swapVal].back()
      • 将待交换的元素放到待删除的元素那里去:
      • 删除元素

注意:

  • 我们在建立map的映射时要用堆而不是普通的vector数组,
    • 因为我们每次remove操作后都会移除nums数组的尾元素,如果我们用vector来保存数组的指标,而且只移出末尾数字的话,有可能出现前面的坐标大小超过了此时 nums 的大小的情况,就会出错
    • 所以我们用优先队列对所有的相同数字的坐标进行自动排序,每次把最大位置的坐标移出即可
class RandomizedCollection {
    std::vector<int> nums;
    std::unordered_map<int, std::priority_queue<int>> m;
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 deleteIdx = m[val].top();
        m[val].pop();
        if(nums.size() - 1 != deleteIdx){
            int swapVal = nums.back();
            nums[deleteIdx] = swapVal;
            m[swapVal].pop();
            m[swapVal].push(deleteIdx);
        }
        nums.pop_back();
        return true;
    }

    int getRandom() {
        return nums[rand() % nums.size()];
    }
};

注意到优先队列的 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;
};

类似题目

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值