20240115-插入删除 GetRandom O(1)

题目要求

实现 RandomizedSet 类:

  • RandomizedSet() 初始化 RandomizedSet 对象。
  • bool insert(int val) 将不存在的项目 val 插入随机集合。如果项目不存在,则返回 true,否则返回 false。
  • bool remove(int val) 从集合中删除项目 val(如果存在)。如果存在,则返回 true,否则返回 false。
  • int getRandom() 从当前元素集中随机返回一个元素(保证调用此方法时至少有一个元素存在)。每个元素被返回的概率必须相同。

您必须实现该类的函数,使每个函数的平均工作时间复杂度为 O(1)。

思路

我不会做,不是很熟悉,参考labuladong,东哥的思路和题解。常数时间删除-查找数组中的任意元素 | labuladong 的算法笔记Info 数据结构精品课 (https://aep.h5.xeknow.com/s/1XJHEO) 和 递归算法专题课 (https://aep.xet.tech/s/3YGcq3) 限时附赠网站会员;算法可视化编辑器上线,点击体验 (https://labuladong.online/algo-visualize/)! 读完本文,你不仅学会了算法套路...icon-default.png?t=N7T8https://labuladong.github.io/algo/di-yi-zhan-da78c/shou-ba-sh-48c1d/chang-shu--6b296/#%E5%AE%9E%E7%8E%B0%E9%9A%8F%E6%9C%BA%E9%9B%86%E5%90%88

对于一个标准的 HashSet,你能否在 O(1) 的时间内实现 getRandom 函数?

其实是不能的,因为根据刚才说到的底层实现,元素是被哈希函数「分散」到整个数组里面的,更别说还有拉链法等等解决哈希冲突的机制,基本做不到 O(1) 时间等概率随机获取元素。

换句话说,对于 getRandom 方法,如果想「等概率」且「在 O(1) 的时间」取出元素,一定要满足:

底层用数组实现,且数组必须是紧凑的,这样我们就可以直接生成随机数作为索引,选取随机元素。

但如果用数组存储元素的话,常规的插入,删除的时间复杂度又不可能是 O(1)

然而,对数组尾部进行插入和删除操作不会涉及数据搬移,时间复杂度是 O(1)

所以,如果我们想在 O(1) 的时间删除数组中的某一个元素 val,可以先把这个元素交换到数组的尾部,然后再 pop 掉。

代码

class RandomizedSet {
public:
    vector<int> nums;
    // 记录每个元素对应在 nums 中的索引
    unordered_map<int, int> valToIndex;

    RandomizedSet() {
        
    }
    
    bool insert(int val) {
        if (valToIndex.count(val)) {
            return false;
        }
        // 若 val 不存在,插入到 nums 尾部,
        // 并记录 val 对应的索引值
        valToIndex[val] = nums.size();
        nums.push_back(val);
        return true;
    }
    
    bool remove(int val) {
        if (!valToIndex.count(val)) {
            return false;
        }
        int index = valToIndex[val];
        // 将最后一个元素对应的索引修改为 index
        valToIndex[nums.back()] = index;
        // 交换 val 和最后一个元素
        swap(nums[index], nums.back());
        // 在数组中删除元素 val
        nums.pop_back();
        // 删除元素 val 对应的索引
        valToIndex.erase(val);
        return true;
    }
    
    int getRandom() {
        // 随机获取 nums 中的一个元素
        return nums[rand() % nums.size()];
    }
};

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

时空复杂度

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值